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 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 145 145: def call_command(cmd, options = {}, &block) #:yields: io 146: case RUBY_PLATFORM 147: when /mswin32|bccwin32/ 148: call_command_popen(cmd, options, &block) 149: else 150: call_command_fork(cmd, options, &block) 151: end 152: end
Executes the program via fork (by using IO.popen("-")) and exec. A block must be given. An IO object is passed to the block.
From the view point of security, this method is recommended rather than call_command_popen.
Arguments:
Returns: | (undefined) |
# File lib/bio/command.rb, line 196 196: def call_command_fork(cmd, options = {}) 197: dir = options[:chdir] 198: cmd = safe_command_line_array(cmd) 199: IO.popen("-", "r+") do |io| 200: if io then 201: # parent 202: yield io 203: else 204: # child 205: # chdir to options[:chdir] if available 206: begin 207: Dir.chdir(dir) if dir 208: rescue Exception 209: Process.exit!(1) 210: end 211: # executing the command 212: begin 213: Kernel.exec(*cmd) 214: rescue Errno::ENOENT, Errno::EACCES 215: Process.exit!(127) 216: rescue Exception 217: end 218: Process.exit!(1) 219: end 220: end 221: 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 232 232: def call_command_open3(cmd) 233: cmd = safe_command_line_array(cmd) 234: Open3.popen3(*cmd) do |pin, pout, perr| 235: yield pin, pout, perr 236: end 237: end
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.
Arguments:
Returns: | (undefined) |
# File lib/bio/command.rb, line 161 161: def call_command_popen(cmd, options = {}) 162: str = make_command_line(cmd) 163: # processing options 164: if dir = options[:chdir] then 165: case RUBY_PLATFORM 166: when /mswin32|bccwin32/ 167: # Unix-like dir separator is changed to Windows dir separator 168: # by using String#gsub. 169: dirstr = dir.gsub(/\//, "\\") 170: chdirstr = make_command_line([ 'cd', '/D', dirstr ]) 171: str = chdirstr + ' && ' + str 172: else 173: # UNIX shell 174: chdirstr = make_command_line([ 'cd', dir ]) 175: str = chdirstr + ' && ' + str 176: end 177: end 178: # call command by using IO.popen 179: IO.popen(str, "w+") do |io| 180: io.sync = true 181: yield io 182: end 183: end
Escape special characters in command line string for UNIX shells.
Arguments:
Returns: | String object |
# File lib/bio/command.rb, line 57 57: def escape_shell_unix(str) 58: str = str.to_s 59: raise 'cannot escape control characters' if UNESCAPABLE_CHARS =~ str 60: str.gsub(UNSAFE_CHARS_UNIX) { |x| "\\#{x}" } 61: end
Escape special characters in command line string for cmd.exe on Windows.
Arguments:
Returns: | String object |
# File lib/bio/command.rb, line 42 42: def escape_shell_windows(str) 43: str = str.to_s 44: raise 'cannot escape control characters' if UNESCAPABLE_CHARS =~ str 45: if QUOTE_CHARS_WINDOWS =~ str then 46: '"' + str.gsub(/\"/, '""') + '"' 47: else 48: String.new(str) 49: end 50: 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 565 565: def http_post_form(http, path, params = nil, header = {}) 566: data = make_cgi_params(params) 567: 568: hash = { 569: 'Content-Type' => 'application/x-www-form-urlencoded', 570: 'Content-Length' => data.length.to_s 571: } 572: hash.update(header) 573: 574: http.post(path, data, hash) 575: end
Builds parameter string for from Hash of parameters for application/x-www-form-urlencoded.
Arguments:
Returns: | String |
# File lib/bio/command.rb, line 619 619: def make_cgi_params(params) 620: data = "" 621: case params 622: when Hash 623: data = params.map do |key, val| 624: make_cgi_params_key_value(key, val) 625: end.join('&') 626: when Array 627: case params.first 628: when Hash 629: data = params.map do |hash| 630: hash.map do |key, val| 631: make_cgi_params_key_value(key, val) 632: end 633: end.join('&') 634: when Array 635: data = params.map do |key, val| 636: make_cgi_params_key_value(key, val) 637: end.join('&') 638: when String 639: data = params.map do |str| 640: key, val = str.split(/\=/, 2) 641: if val then 642: make_cgi_params_key_value(key, val) 643: else 644: CGI.escape(str) 645: end 646: end.join('&') 647: end 648: when String 649: data = URI.escape(params.strip) 650: end 651: return data 652: 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 662 662: def make_cgi_params_key_value(key, value) 663: result = [] 664: case value 665: when Array 666: value.each do |val| 667: result << [key, val].map {|x| CGI.escape(x.to_s) }.join('=') 668: end 669: else 670: result << [key, value].map {|x| CGI.escape(x.to_s) }.join('=') 671: end 672: return result 673: end
Generate command line string with special characters escaped.
Arguments:
Returns: | String object |
# File lib/bio/command.rb, line 82 82: def make_command_line(ary) 83: case RUBY_PLATFORM 84: when /mswin32|bccwin32/ 85: make_command_line_windows(ary) 86: else 87: make_command_line_unix(ary) 88: end 89: end
Generate command line string with special characters escaped for UNIX shells.
Arguments:
Returns: | String object |
# File lib/bio/command.rb, line 107 107: def make_command_line_unix(ary) 108: ary.collect { |str| escape_shell_unix(str) }.join(" ") 109: end
Generate command line string with special characters escaped for cmd.exe on Windows.
Arguments:
Returns: | String object |
# File lib/bio/command.rb, line 97 97: def make_command_line_windows(ary) 98: ary.collect { |str| escape_shell_windows(str) }.join(" ") 99: 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 367 367: def mktmpdir(prefix_suffix = nil, tmpdir = nil, &block) 368: begin 369: Dir.mktmpdir(prefix_suffix, tmpdir, &block) 370: rescue NoMethodError 371: # backported from Ruby 1.9.2-preview1. 372: # ***** Below is excerpted from Ruby 1.9.2-preview1's lib/tmpdir.rb **** 373: # ***** Be careful about copyright. **** 374: case prefix_suffix 375: when nil 376: prefix = "d" 377: suffix = "" 378: when String 379: prefix = prefix_suffix 380: suffix = "" 381: when Array 382: prefix = prefix_suffix[0] 383: suffix = prefix_suffix[1] 384: else 385: raise ArgumentError, "unexpected prefix_suffix: #{prefix_suffix.inspect}" 386: end 387: tmpdir ||= Dir.tmpdir 388: t = Time.now.strftime("%Y%m%d") 389: n = nil 390: begin 391: path = "#{tmpdir}/#{prefix}#{t}-#{$$}-#{rand(0x100000000).to_s(36)}" 392: path << "-#{n}" if n 393: path << suffix 394: Dir.mkdir(path, 0700) 395: rescue Errno::EEXIST 396: n ||= 0 397: n += 1 398: retry 399: end 400: 401: if block_given? 402: begin 403: yield path 404: ensure 405: remove_entry_secure path 406: end 407: else 408: path 409: end 410: # ***** Above is excerpted from Ruby 1.9.2-preview1's lib/tmpdir.rb **** 411: end 412: 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 535 535: def new_http(address, port = 80) 536: uri = URI.parse("http://#{address}:#{port}") 537: # Note: URI#find_proxy is an unofficial method defined in open-uri.rb. 538: # If the spec of open-uri.rb would be changed, we should change below. 539: if proxyuri = uri.find_proxy then 540: raise 'Non-HTTP proxy' if proxyuri.class != URI::HTTP 541: Net::HTTP.new(address, port, proxyuri.host, proxyuri.port) 542: else 543: Net::HTTP.new(address, port) 544: end 545: 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 594 594: def post_form(uri, params = nil, header = {}) 595: unless uri.is_a?(URI) 596: uri = URI.parse(uri) 597: end 598: 599: data = make_cgi_params(params) 600: 601: hash = { 602: 'Content-Type' => 'application/x-www-form-urlencoded', 603: 'Content-Length' => data.length.to_s 604: } 605: hash.update(header) 606: 607: start_http(uri.host, uri.port) do |http| 608: http.post(uri.path, data, hash) 609: end 610: 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 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 254 254: def query_command(cmd, query = nil, options = {}) 255: case RUBY_PLATFORM 256: when /mswin32|bccwin32/ 257: query_command_popen(cmd, query, options) 258: else 259: query_command_fork(cmd, query, options) 260: end 261: 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.
Fork (by using IO.popen("-")) and exec is used to execute the program.
From the view point of security, this method is recommended rather than query_command_popen.
Arguments:
Returns: | String or nil |
# File lib/bio/command.rb, line 301 301: def query_command_fork(cmd, query = nil, options = {}) 302: ret = nil 303: call_command_fork(cmd, options) do |io| 304: io.sync = true 305: io.print query if query 306: io.close_write 307: ret = io.read 308: end 309: ret 310: 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 323 323: def query_command_open3(cmd, query = nil) 324: errorlog = nil 325: cmd = safe_command_line_array(cmd) 326: Open3.popen3(*cmd) do |pin, pout, perr| 327: perr.sync = true 328: t = Thread.start { errorlog = perr.read } 329: begin 330: pin.print query if query 331: pin.close 332: output = pout.read 333: ensure 334: t.join 335: end 336: [ output, errorlog ] 337: end 338: 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.
IO.popen is used for OS which doesn‘t support fork.
Arguments:
Returns: | String or nil |
# File lib/bio/command.rb, line 275 275: def query_command_popen(cmd, query = nil, options = {}) 276: ret = nil 277: call_command_popen(cmd, options) do |io| 278: io.sync = true 279: io.print query if query 280: io.close_write 281: ret = io.read 282: end 283: ret 284: 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 349 349: def remove_entry_secure(path, force = false) 350: begin 351: FileUtils.remove_entry_secure(path, force) 352: rescue NoMethodError 353: 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}" 354: nil 355: end 356: 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 118 118: def safe_command_line_array(ary) 119: ary = ary.to_ary 120: return ary if ary.size >= 2 or ary.empty? 121: if ary.size != 1 then 122: raise 'Bug: assersion of ary.size == 1 failed' 123: end 124: arg0 = ary[0] 125: begin 126: arg0 = arg0.to_ary 127: rescue NoMethodError 128: arg0 = [ arg0, arg0 ] 129: end 130: [ arg0 ] 131: 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 511 511: def start_http(address, port = 80, &block) 512: uri = URI.parse("http://#{address}:#{port}") 513: # Note: URI#find_proxy is an unofficial method defined in open-uri.rb. 514: # If the spec of open-uri.rb would be changed, we should change below. 515: if proxyuri = uri.find_proxy then 516: raise 'Non-HTTP proxy' if proxyuri.class != URI::HTTP 517: http = Net::HTTP.Proxy(proxyuri.host, proxyuri.port) 518: else 519: http = Net::HTTP 520: end 521: http.start(address, port, &block) 522: end