Module | Bio::Command
In: | lib/bio/command.rb

# Bio::Command

Bio::Command is a collection of useful methods for execution of external commands or web applications. Any wrapper class for applications shall use this class.

Library internal use only. Users should not directly use it.

### Methods

call_command call_command_fork call_command_open3 call_command_popen escape_shell escape_shell_unix escape_shell_windows http_post_form make_cgi_params make_cgi_params_key_value make_command_line make_command_line_unix make_command_line_windows mktmpdir new_http no_fork? post_form query_command query_command_fork query_command_open3 query_command_popen read_uri remove_entry_secure safe_command_line_array start_http windows_platform?

### Classes and Modules

Class Bio::Command::Tmpdir

### Constants

UNSAFE_CHARS_UNIX | = | /[^A-Za-z0-9\_\-\.\:\,\/\@\x1b\x80-\xfe]/n
QUOTE_CHARS_WINDOWS | = | /[^A-Za-z0-9\_\-\.\:\,\/\@\\]/n
UNESCAPABLE_CHARS | = | /[\x00-\x08\x10-\x1a\x1c-\x1f\x7f\xff]/n

### Public Instance methods

call_command(cmd, options = {}) {|io| ...}

Executes the program. Automatically select popen for Ruby 1.9 or Windows environment and fork for the others. A block must be given. An IO object is passed to the block.

Available options:

      :chdir => "path" : changes working directory to the specified path.

Arguments:

- (required) cmd: Array containing String objects
- (optional) options: Hash

Returns: | (undefined)

[Source]

         # File lib/bio/command.rb, line 196
    196:   def call_command(cmd, options = {}, &block) #:yields: io
    197:     if RUBY_VERSION >= "1.9.0" then
    198:       return call_command_popen(cmd, options, &block)
    199:     elsif no_fork? then
    200:       call_command_popen(cmd, options, &block)
    201:     else
    202:       begin
    203:         call_command_fork(cmd, options, &block)
    204:       rescue NotImplementedError
    205:         # fork(2) not implemented
    206:         @@no_fork = true
    207:         call_command_popen(cmd, options, &block)
    208:       end
    209:     end
    210:   end

call_command_fork(cmd, options = {}) {|io| ...}

This method is internally called from the call_command method. In normal case, use call_command, and do not call this method directly.

Executes the program via fork (by using IO.popen("-")) and exec. A block must be given. An IO object is passed to the block.

See the document of call_command for available options.

Note for Ruby 1.8: In Ruby 1.8, from the view point of security, this method is recommended rather than call_command_popen. However, this method might have problems with multi-threads.

Note for Ruby 1.9: In Ruby 1.9, this method can not be used, because Thread.critical is removed. In Ruby 1.9, call_command_popen is safe and robust enough, and is the recommended way, because IO.popen is improved to get a command-line as an array without calling shell.

Arguments:

- (required) cmd: Array containing String objects
- (optional) options: Hash

Returns: | (undefined)

[Source]

         # File lib/bio/command.rb, line 295
    295:   def call_command_fork(cmd, options = {})
    296:     dir = options[:chdir]
    297:     cmd = safe_command_line_array(cmd)
    298:     begin
    299:     tc, Thread.critical, flag0, flag1 = Thread.critical, true, true, true
    300:     IO.popen("-", "r+") do |io|
    301:       if io then
    302:         # parent
    303:         flag0, Thread.critical, flag1 = false, tc, false
    304:         yield io
    305:       else
    306:         # child
    307:         Thread.critical = true # for safety, though already true
    308:         GC.disable
    309:         # chdir to options[:chdir] if available
    310:         begin
    311:           Dir.chdir(dir) if dir
    312:         rescue Exception
    313:           Process.exit!(1)
    314:         end
    315:         # executing the command
    316:         begin
    317:           Kernel.exec(*cmd)
    318:         rescue Errno::ENOENT, Errno::EACCES
    319:           Process.exit!(127)
    320:         rescue Exception
    321:         end
    322:         Process.exit!(1)
    323:       end
    324:     end
    325:     ensure
    326:       # When IO.popen("-") raises error, Thread.critical will be set here.
    327:       Thread.critical = tc if flag0 or flag1
    328:       #warn 'Thread.critical might have wrong value.' if flag0 != flag1
    329:     end
    330:   end

call_command_open3(cmd) {|pin, pout, perr| ...}

Executes the program via Open3.popen3 A block must be given. IO objects are passed to the block.

You would use this method only when you really need to get stderr.

Arguments:

- (required) cmd: Array containing String objects

Returns: | (undefined)

[Source]

         # File lib/bio/command.rb, line 341
    341:   def call_command_open3(cmd)
    342:     cmd = safe_command_line_array(cmd)
    343:     Open3.popen3(*cmd) do |pin, pout, perr|
    344:       yield pin, pout, perr
    345:     end
    346:   end

call_command_popen(cmd, options = {}) {|io| ...}

This method is internally called from the call_command method. In normal case, use call_command, and do not call this method directly.

Executes the program via IO.popen for OS which doesn‘t support fork. A block must be given. An IO object is passed to the block.

See the document of call_command for available options.

Note for Ruby 1.8: In Ruby 1.8, although shell unsafe characters are escaped. If inescapable characters exists, it raises RuntimeError. So, call_command_fork is normally recommended.

Note for Ruby 1.9: In Ruby 1.9, call_command_popen is safe and robust enough, and is the recommended way, because IO.popen is improved to get a command-line as an array without calling shell.

Arguments:

- (required) cmd: Array containing String objects
- (optional) options: Hash

Returns: | (undefined)

[Source]

         # File lib/bio/command.rb, line 235
    235:   def call_command_popen(cmd, options = {})
    236:     if RUBY_VERSION >= "1.9.0" then
    237:       # For Ruby 1.9 or later, using command line array with options.
    238:       dir = options[:chdir]
    239:       cmd = safe_command_line_array(cmd)
    240:       if dir then
    241:         cmd = cmd + [ { :chdir => dir } ]
    242:       end
    243:       r = IO.popen(cmd, "r+") do |io|
    244:         yield io
    245:       end
    246:       return r
    247:     end
    248:     # For Ruby 1.8, using command line string.
    249:     str = make_command_line(cmd)
    250:     # processing options
    251:     if dir = options[:chdir] then
    252:       if windows_platform?
    253:         # Unix-like dir separator is changed to Windows dir separator
    254:         # by using String#gsub.
    255:         dirstr = dir.gsub(/\//, "\\")
    256:         chdirstr = make_command_line([ 'cd', '/D', dirstr ])
    257:         str = chdirstr + ' && ' + str
    258:       else
    259:         # UNIX shell
    260:         chdirstr = make_command_line([ 'cd', dir ])
    261:         str = chdirstr + ' && ' + str
    262:       end
    263:     end
    264:     # call command by using IO.popen
    265:     IO.popen(str, "w+") do |io|
    266:       io.sync = true
    267:       yield io
    268:     end
    269:   end

escape_shell(str)

Escape special characters in command line string.

Arguments:

- (required) str: String

Returns: | String object

[Source]

         # File lib/bio/command.rb, line 121
    121:   def escape_shell(str)
    122:     if windows_platform? then
    123:       escape_shell_windows(str)
    124:     else
    125:       escape_shell_unix(str)
    126:     end
    127:   end

escape_shell_unix(str)

Escape special characters in command line string for UNIX shells.

Arguments:

- (required) str: String

Returns: | String object

[Source]

         # File lib/bio/command.rb, line 110
    110:   def escape_shell_unix(str)
    111:     str = str.to_s
    112:     raise 'cannot escape control characters' if UNESCAPABLE_CHARS =~ str
    113:     str.gsub(UNSAFE_CHARS_UNIX) { |x| "\\#{x}" }
    114:   end

escape_shell_windows(str)

Escape special characters in command line string for cmd.exe on Windows.

Arguments:

- (required) str: String

Returns: | String object

[Source]

         # File lib/bio/command.rb, line 95
     95:   def escape_shell_windows(str)
     96:     str = str.to_s
     97:     raise 'cannot escape control characters' if UNESCAPABLE_CHARS =~ str
     98:     if QUOTE_CHARS_WINDOWS =~ str then
     99:       '"' + str.gsub(/\"/, '""') + '"'
    100:     else
    101:       String.new(str)
    102:     end
    103:   end

http_post_form(http, path, params = nil, header = {})

Same as:

     http = Net::HTTP.new(...); http.post_form(path, params)

and it uses proxy if an environment variable (same as OpenURI.open_uri) is set. In addition, `header` can be set. (Note that Content-Type and Content-Length are automatically set by default.) `uri` must be a URI object, `params` must be a hash, and `header` must be a hash.

Arguments:

- (required) http: Net::HTTP object or compatible object
- (required) path: String
- (optional) params: Hash containing parameters
- (optional) header: Hash containing header strings

Returns: | (same as Net::HTTP::post_form)

[Source]

         # File lib/bio/command.rb, line 693
    693:   def http_post_form(http, path, params = nil, header = {})
    694:     data = make_cgi_params(params)
    695:
    696:     hash = {
    697:       'Content-Type'   => 'application/x-www-form-urlencoded',
    698:       'Content-Length' => data.length.to_s
    699:     }
    700:     hash.update(header)
    701:
    702:     http.post(path, data, hash)
    703:   end

make_cgi_params(params)

Builds parameter string for from Hash of parameters for application/x-www-form-urlencoded.

Arguments:

- (required) params: Hash containing parameters

Returns: | String

[Source]

         # File lib/bio/command.rb, line 747
    747:   def make_cgi_params(params)
    748:     data = ""
    749:     case params
    750:     when Hash
    751:       data = params.map do |key, val|
    752:         make_cgi_params_key_value(key, val)
    753:       end.join('&')
    754:     when Array
    755:       case params.first
    756:       when Hash
    757:         data = params.map do |hash|
    758:           hash.map do |key, val|
    759:             make_cgi_params_key_value(key, val)
    760:           end
    761:         end.join('&')
    762:       when Array
    763:         data = params.map do |key, val|
    764:           make_cgi_params_key_value(key, val)
    765:         end.join('&')
    766:       when String
    767:         data = params.map do |str|
    768:           key, val = str.split(/\=/, 2)
    769:           if val then
    770:             make_cgi_params_key_value(key, val)
    771:           else
    772:             CGI.escape(str)
    773:           end
    774:         end.join('&')
    775:       end
    776:     when String
    777:       data = URI.escape(params.strip)
    778:     end
    779:     return data
    780:   end

make_cgi_params_key_value(key, value)

Builds parameter string for from a key string and a value (or values) for application/x-www-form-urlencoded.

Arguments:

- (required) key: String
- (required) value: String or Array containing String

Returns: | String

[Source]

         # File lib/bio/command.rb, line 790
    790:   def make_cgi_params_key_value(key, value)
    791:     result = []
    792:     case value
    793:     when Array
    794:       value.each do |val|
    795:         result << [key, val].map {|x| CGI.escape(x.to_s) }.join('=')
    796:       end
    797:     else
    798:       result << [key, value].map {|x| CGI.escape(x.to_s) }.join('=')
    799:     end
    800:     return result
    801:   end

make_command_line(ary)

Generate command line string with special characters escaped.

Arguments:

- (required) ary: Array containing String objects

Returns: | String object

[Source]

         # File lib/bio/command.rb, line 134
    134:   def make_command_line(ary)
    135:     if windows_platform? then
    136:       make_command_line_windows(ary)
    137:     else
    138:       make_command_line_unix(ary)
    139:     end
    140:   end

make_command_line_unix(ary)

Generate command line string with special characters escaped for UNIX shells.

Arguments:

- (required) ary: Array containing String objects

Returns: | String object

[Source]

         # File lib/bio/command.rb, line 158
    158:   def make_command_line_unix(ary)
    159:     ary.collect { |str| escape_shell_unix(str) }.join(" ")
    160:   end

make_command_line_windows(ary)

Generate command line string with special characters escaped for cmd.exe on Windows.

Arguments:

- (required) ary: Array containing String objects

Returns: | String object

[Source]

         # File lib/bio/command.rb, line 148
    148:   def make_command_line_windows(ary)
    149:     ary.collect { |str| escape_shell_windows(str) }.join(" ")
    150:   end

mktmpdir(prefix_suffix = nil, tmpdir = nil) {|path| ...}

Backport of Dir.mktmpdir in Ruby 1.9.

Same as Dir.mktmpdir(prefix_suffix) in Ruby 1.9.

Arguments:

- (optional) prefix_suffix: String (or Array, etc.)
- (optional) tmpdir: String: temporary directory‘s path

[Source]

         # File lib/bio/command.rb, line 495
    495:   def mktmpdir(prefix_suffix = nil, tmpdir = nil, &block)
    496:     begin
    497:       Dir.mktmpdir(prefix_suffix, tmpdir, &block)
    498:     rescue NoMethodError
    499:       # backported from Ruby 1.9.2-preview1.
    500:       # ***** Below is excerpted from Ruby 1.9.2-preview1's lib/tmpdir.rb ****
    501:       # ***** Be careful about copyright. ****
    502:       case prefix_suffix
    503:       when nil
    504:         prefix = "d"
    505:         suffix = ""
    506:       when String
    507:         prefix = prefix_suffix
    508:         suffix = ""
    509:       when Array
    510:         prefix = prefix_suffix[0]
    511:         suffix = prefix_suffix[1]
    512:       else
    513:         raise ArgumentError, "unexpected prefix_suffix: #{prefix_suffix.inspect}"
    514:       end
    515:       tmpdir ||= Dir.tmpdir
    516:       t = Time.now.strftime("%Y%m%d")
    517:       n = nil
    518:       begin
    519:         path = "#{tmpdir}/#{prefix}#{t}-#{$$}-#{rand(0x100000000).to_s(36)}"
    520:         path << "-#{n}" if n
    521:         path << suffix
    522:         Dir.mkdir(path, 0700)
    523:       rescue Errno::EEXIST
    524:         n ||= 0
    525:         n += 1
    526:         retry
    527:       end
    528:
    529:       if block_given?
    530:         begin
    531:           yield path
    532:         ensure
    533:           remove_entry_secure path
    534:         end
    535:       else
    536:         path
    537:       end
    538:       # ***** Above is excerpted from Ruby 1.9.2-preview1's lib/tmpdir.rb ****
    539:     end
    540:   end

new_http(address, port = 80)

Same as:

      Net::HTTP.new(address, port)

and it uses proxy if an environment variable (same as OpenURI.open_uri) is set.

Arguments:

- (required) address: String containing host name or IP address
- (optional) port: port (sanme as Net::HTTP::start)

Returns: | (same as Net::HTTP.new except for proxy support)

[Source]

         # File lib/bio/command.rb, line 663
    663:   def new_http(address, port = 80)
    664:     uri = URI.parse("http://#{address}:#{port}")
    665:     # Note: URI#find_proxy is an unofficial method defined in open-uri.rb.
    666:     # If the spec of open-uri.rb would be changed, we should change below.
    667:     if proxyuri = uri.find_proxy then
    668:       raise 'Non-HTTP proxy' if proxyuri.class != URI::HTTP
    669:       Net::HTTP.new(address, port, proxyuri.host, proxyuri.port)
    670:     else
    671:       Net::HTTP.new(address, port)
    672:     end
    673:   end

no_fork?()

CAUTION Bio::Command INTERNAL USE ONLY. Users must NOT use the method. The method will be removed when it is not needed.

Checks if the OS does not support fork(2) system call. When not supported, it returns true. When supported or unknown, it returns false or nil.

Known issues:

- It might make a mistake in minor platforms/architectures/interpreters.

Returns: | true, false or nil.

[Source]

        # File lib/bio/command.rb, line 80
    80:   def no_fork?
    81:     if (defined?(@@no_fork) && @@no_fork) or
    82:         windows_platform? or /java/i =~ RUBY_PLATFORM then
    83:       true
    84:     else
    85:       false
    86:     end
    87:   end

post_form(uri, params = nil, header = {})

Same as: Net::HTTP.post_form(uri, params) and it uses proxy if an environment variable (same as OpenURI.open_uri) is set. In addition, `header` can be set. (Note that Content-Type and Content-Length are automatically set by default.) `uri` must be a URI object, `params` must be a hash, and `header` must be a hash.

Arguments:

- (required) uri: URI object or String
- (optional) params: Hash containing parameters
- (optional) header: Hash containing header strings

Returns: | (same as Net::HTTP::post_form)

[Source]

         # File lib/bio/command.rb, line 722
    722:   def post_form(uri, params = nil, header = {})
    723:     unless uri.is_a?(URI)
    724:       uri = URI.parse(uri)
    725:     end
    726:
    727:     data = make_cgi_params(params)
    728:
    729:     hash = {
    730:       'Content-Type'   => 'application/x-www-form-urlencoded',
    731:       'Content-Length' => data.length.to_s
    732:     }
    733:     hash.update(header)
    734:
    735:     start_http(uri.host, uri.port) do |http|
    736:       http.post(uri.path, data, hash)
    737:     end
    738:   end

query_command(cmd, query = nil, options = {})

Executes the program with the query (String) given to the standard input, waits the program termination, and returns the output data printed to the standard output as a string.

Automatically select popen for Ruby 1.9 or Windows environment and fork for the others.

Available options:

      :chdir => "path" : changes working directory to the specified path.

Arguments:

- (required) cmd: Array containing String objects
- (optional) query: String
- (optional) options: Hash

Returns: | String or nil

[Source]

         # File lib/bio/command.rb, line 364
    364:   def query_command(cmd, query = nil, options = {})
    365:     if RUBY_VERSION >= "1.9.0" then
    366:       return query_command_popen(cmd, query, options)
    367:     elsif no_fork? then
    368:       query_command_popen(cmd, query, options)
    369:     else
    370:       begin
    371:         query_command_fork(cmd, query, options)
    372:       rescue NotImplementedError
    373:         # fork(2) not implemented
    374:         @@no_fork = true
    375:         query_command_fork(cmd, query, options)
    376:       end
    377:     end
    378:   end

query_command_fork(cmd, query = nil, options = {})

This method is internally called from the query_command method. In normal case, use query_command, and do not call this method directly.

Executes the program with the query (String) given to the standard input, waits the program termination, and returns the output data printed to the standard output as a string.

Fork (by using IO.popen("-")) and exec is used to execute the program.

See the document of query_command for available options.

See the document of call_command_fork for the security and Ruby version specific issues.

Arguments:

- (required) cmd: Array containing String objects
- (optional) query: String
- (optional) options: Hash

Returns: | String or nil

[Source]

         # File lib/bio/command.rb, line 429
    429:   def query_command_fork(cmd, query = nil, options = {})
    430:     ret = nil
    431:     call_command_fork(cmd, options) do |io|
    432:       io.sync = true
    433:       io.print query if query
    434:       io.close_write
    435:       ret = io.read
    436:     end
    437:     ret
    438:   end

query_command_open3(cmd, query = nil)

Executes the program via Open3.popen3 with the query (String) given to the stain, waits the program termination, and returns the data from stdout and stderr as an array of the strings.

You would use this method only when you really need to get stderr.

Arguments:

- (required) cmd: Array containing String objects
- (optional) query: String

Returns: | Array containing 2 objects: output string (or nil) and stderr string (or nil)

[Source]

         # File lib/bio/command.rb, line 451
    451:   def query_command_open3(cmd, query = nil)
    452:     errorlog = nil
    453:     cmd = safe_command_line_array(cmd)
    454:     Open3.popen3(*cmd) do |pin, pout, perr|
    455:       perr.sync = true
    456:       t = Thread.start { errorlog = perr.read }
    457:       begin
    458:         pin.print query if query
    459:         pin.close
    460:         output = pout.read
    461:       ensure
    462:         t.join
    463:       end
    464:       [ output, errorlog ]
    465:     end
    466:   end

query_command_popen(cmd, query = nil, options = {})

This method is internally called from the query_command method. In normal case, use query_command, and do not call this method directly.

Executes the program with the query (String) given to the standard input, waits the program termination, and returns the output data printed to the standard output as a string.

See the document of query_command for available options.

See the document of call_command_popen for the security and Ruby version specific issues.

Arguments:

- (required) cmd: Array containing String objects
- (optional) query: String
- (optional) options: Hash

Returns: | String or nil

[Source]

         # File lib/bio/command.rb, line 398
    398:   def query_command_popen(cmd, query = nil, options = {})
    399:     ret = nil
    400:     call_command_popen(cmd, options) do |io|
    401:       io.sync = true
    402:       io.print query if query
    403:       io.close_write
    404:       ret = io.read
    405:     end
    406:     ret
    407:   end

read_uri(uri)

Same as OpenURI.open_uri(uri).read and it uses proxy if an environment variable (same as OpenURI.open_uri) is set.

Arguments:

- (required) uri: URI object or String

Returns: | String

[Source]

         # File lib/bio/command.rb, line 624
    624:   def read_uri(uri)
    625:     OpenURI.open_uri(uri).read
    626:   end

remove_entry_secure(path, force = false)

Same as FileUtils.remove_entry_secure after Ruby 1.8.3. In Ruby 1.8.2 or previous version, it only shows warning message and does nothing.

It is strongly recommended using Ruby 1.8.5 or later.

Arguments:

- (required) path: String
- (optional) force: boolean

[Source]

         # File lib/bio/command.rb, line 477
    477:   def remove_entry_secure(path, force = false)
    478:     begin
    479:       FileUtils.remove_entry_secure(path, force)
    480:     rescue NoMethodError
    481:       warn "The temporary file or directory is not removed because of the lack of FileUtils.remove_entry_secure. Use Ruby 1.8.3 or later (1.8.5 or later is strongly recommended): #{path}"
    482:       nil
    483:     end
    484:   end

safe_command_line_array(ary)

Returns an Array of command-line command and arguments that can be safely passed to Kernel.exec etc. If the given array is already safe (or empty), returns the given array.

Arguments:

- (required) ary: Array

Returns: | Array

[Source]

         # File lib/bio/command.rb, line 169
    169:   def safe_command_line_array(ary)
    170:     ary = ary.to_ary
    171:     return ary if ary.size >= 2 or ary.empty?
    172:     if ary.size != 1 then
    173:       raise 'Bug: assersion of ary.size == 1 failed'
    174:     end
    175:     arg0 = ary[0]
    176:     begin
    177:       arg0 = arg0.to_ary
    178:     rescue NoMethodError
    179:       arg0 = [ arg0, arg0 ]
    180:     end
    181:     [ arg0 ]
    182:   end

start_http(address, port = 80, &block)

Same as:

      Net::HTTP.start(address, port)

and it uses proxy if an environment variable (same as OpenURI.open_uri) is set.

Arguments:

- (required) address: String containing host name or IP address
- (optional) port: port (sanme as Net::HTTP::start)

Returns: | (same as Net::HTTP::start except for proxy support)

[Source]

         # File lib/bio/command.rb, line 639
    639:   def start_http(address, port = 80, &block)
    640:     uri = URI.parse("http://#{address}:#{port}")
    641:     # Note: URI#find_proxy is an unofficial method defined in open-uri.rb.
    642:     # If the spec of open-uri.rb would be changed, we should change below.
    643:     if proxyuri = uri.find_proxy then
    644:       raise 'Non-HTTP proxy' if proxyuri.class != URI::HTTP
    645:       http = Net::HTTP.Proxy(proxyuri.host, proxyuri.port)
    646:     else
    647:       http = Net::HTTP
    648:     end
    649:     http.start(address, port, &block)
    650:   end

windows_platform?()

CAUTION Bio::Command INTERNAL USE ONLY. Users must NOT use the method. The method will be removed when it is not needed.

Checks if the program is running on Microsoft Windows. If Windows, returns true. Otherwise, returns false. Note that Cygwin is not treated as Windows.

Known issues:

- It might make a mistake in minor platforms/architectures/interpreters.
- When running JRuby on Cygwin, the result is unknown.

Returns: | true or false

[Source]

        # File lib/bio/command.rb, line 50
    50:   def windows_platform?
    51:     case RUBY_PLATFORM
    52:     when /(?:mswin|bccwin|mingw)(?:32|64)/i
    53:       true
    54:     when /java/i
    55:       # Reference: Redmine's platform.rb
    56:       # http://www.redmine.org/projects/redmine/repository/revisions/1753/entry/trunk/lib/redmine/platform.rb
    57:       if /windows/i =~ (ENV['OS'] || ENV['os']).to_s then
    58:         true
    59:       else
    60:         false
    61:       end
    62:     else
    63:       false
    64:     end
    65:   end

[Validate]