Module | Bio::Command |
In: |
lib/bio/command.rb
|
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.
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 |
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:
Returns: | (undefined) |
# 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:
Returns: | (undefined) |
# 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:
Returns: | (undefined) |
# 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:
Returns: | (undefined) |
# 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 for UNIX shells.
Arguments:
Returns: | String object |
# 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 |
# 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:
Returns: | (same as Net::HTTP::post_form) |
# 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:
Returns: | String |
# 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 |
# 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:
Returns: | String object |
# 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:
Returns: | String object |
# 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:
Returns: | String object |
# 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:
# 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:
Returns: | (same as Net::HTTP.new except for proxy support) |
# 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:
Returns: | true, false or nil. |
# 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:
Returns: | (same as Net::HTTP::post_form) |
# 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:
Returns: | String or nil |
# 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:
Returns: | String or nil |
# 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:
Returns: | Array containing 2 objects: output string (or nil) and stderr string (or nil) |
# 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:
Returns: | String or nil |
# 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 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:
# 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:
Returns: | Array |
# 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:
Returns: | (same as Net::HTTP::start except for proxy support) |
# 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:
Returns: | true or false |
# 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