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

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

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

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

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

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 special characters in command line string.


Arguments:

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 special characters in command line string for UNIX shells.


Arguments:

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 special characters in command line string for cmd.exe on Windows.


Arguments:

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

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

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

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


Arguments:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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]