Module: Bio::Command

Defined in:
lib/bio/command.rb

Overview

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.

Constant Summary collapse

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

Class Method Summary collapse

Class Method Details

.call_command(cmd, &block) ⇒ Object

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.



87
88
89
90
91
92
93
94
# File 'lib/bio/command.rb', line 87

def call_command(cmd, &block)
  case RUBY_PLATFORM
  when /mswin32|bccwin32/
    call_command_popen(cmd, &block)
  else
    call_command_fork(cmd, &block)
  end
end

.call_command_fork(cmd) ⇒ Object

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.



111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
# File 'lib/bio/command.rb', line 111

def call_command_fork(cmd)
  IO.popen("-", "r+") do |io|
    if io then
      # parent
      yield io
    else
      # child
      begin
        Kernel.exec(*cmd)
      rescue Errno::ENOENT, Errno::EACCES
        Process.exit!(127)
      rescue Exception
      end
      Process.exit!(1)
    end
  end
end

.call_command_open3(cmd) ⇒ Object

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.



133
134
135
136
137
138
# File 'lib/bio/command.rb', line 133

def call_command_open3(cmd)
  cmd = cmd.collect { |x| x.to_s }
  Open3.popen3(*cmd) do |pin, pout, perr|
    yield pin, pout, perr
  end
end

.call_command_popen(cmd) ⇒ Object

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.



98
99
100
101
102
103
104
# File 'lib/bio/command.rb', line 98

def call_command_popen(cmd)
  str = make_command_line(cmd)
  IO.popen(str, "w+") do |io|
    io.sync = true
    yield io
  end
end

.escape_shell(str) ⇒ Object

Escape special characters in command line string.



53
54
55
56
57
58
59
60
# File 'lib/bio/command.rb', line 53

def escape_shell(str)
  case RUBY_PLATFORM
  when /mswin32|bccwin32/
    escape_shell_windows(str)
  else
    escape_shell_unix(str)
  end
end

.escape_shell_unix(str) ⇒ Object

Escape special characters in command line string for UNIX shells.



46
47
48
49
50
# File 'lib/bio/command.rb', line 46

def escape_shell_unix(str)
  str = str.to_s
  raise 'cannot escape control characters' if UNESCAPABLE_CHARS =~ str
  str.gsub(UNSAFE_CHARS_UNIX) { |x| "\\#{x}" }
end

.escape_shell_windows(str) ⇒ Object

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



35
36
37
38
39
40
41
42
43
# File 'lib/bio/command.rb', line 35

def escape_shell_windows(str)
  str = str.to_s
  raise 'cannot escape control characters' if UNESCAPABLE_CHARS =~ str
  if QUOTE_CHARS_WINDOWS =~ str then
    '"' + str.gsub(/\"/, '""') + '"'
  else
    String.new(str)
  end
end

.make_cgi_params(params) ⇒ Object



292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
# File 'lib/bio/command.rb', line 292

def make_cgi_params(params)
  data = ""
  case params
  when Hash
    data = params.map do |key, val|
      make_cgi_params_key_value(key, val)
    end.join('&')
  when Array
    case params.first
    when Hash
      data = params.map do |hash|
        hash.map do |key, val|
          make_cgi_params_key_value(key, val)
        end
      end.join('&')
    when Array
      data = params.map do |key, val|
        make_cgi_params_key_value(key, val)
      end.join('&')
    when String
      data = params.map do |str|
        URI.escape(str.strip)
      end.join('&')
    end
  when String
    data = URI.escape(params.strip)
  end
  return data
end

.make_cgi_params_key_value(key, value) ⇒ Object



322
323
324
325
326
327
328
329
330
331
332
333
# File 'lib/bio/command.rb', line 322

def make_cgi_params_key_value(key, value)
  result = []
  case value
  when Array
    value.each do |val|
      result << [key, val].map {|x| URI.escape(x.to_s) }.join('=')
    end
  else
    result << [key, value].map {|x| URI.escape(x.to_s) }.join('=')
  end
  return result
end

.make_command_line(ary) ⇒ Object

Generate command line string with special characters escaped.



63
64
65
66
67
68
69
70
# File 'lib/bio/command.rb', line 63

def make_command_line(ary)
  case RUBY_PLATFORM
  when /mswin32|bccwin32/
    make_command_line_windows(ary)
  else
    make_command_line_unix(ary)
  end
end

.make_command_line_unix(ary) ⇒ Object

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



80
81
82
# File 'lib/bio/command.rb', line 80

def make_command_line_unix(ary)
  ary.collect { |str| escape_shell_unix(str) }.join(" ")
end

.make_command_line_windows(ary) ⇒ Object

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



74
75
76
# File 'lib/bio/command.rb', line 74

def make_command_line_windows(ary)
  ary.collect { |str| escape_shell_windows(str) }.join(" ")
end

.new_http(address, port = 80) ⇒ Object

Same as:

Net::HTTP.new(address, port)

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



251
252
253
254
255
256
257
258
259
260
261
# File 'lib/bio/command.rb', line 251

def new_http(address, port = 80)
  uri = URI.parse("http://#{address}:#{port}")
  # Note: URI#find_proxy is an unofficial method defined in open-uri.rb.
  # If the spec of open-uri.rb would be changed, we should change below.
  if proxyuri = uri.find_proxy then
    raise 'Non-HTTP proxy' if proxyuri.class != URI::HTTP
    Net::HTTP.new(address, port, proxyuri.host, proxyuri.port)
  else
    Net::HTTP.new(address, port)
  end
end

.post_form(uri, params = nil, header = {}) ⇒ Object

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.



274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
# File 'lib/bio/command.rb', line 274

def post_form(uri, params = nil, header = {})
  unless uri.is_a?(URI)
    uri = URI.parse(uri)
  end

  data = make_cgi_params(params)

  hash = {
    'Content-Type'   => 'application/x-www-form-urlencoded',
    'Content-Length' => data.length.to_s
  }
  hash.update(header)

  start_http(uri.host, uri.port) do |http|
    http.post(uri.path, data, hash)
  end
end

.query_command(cmd, query = nil) ⇒ Object

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.



145
146
147
148
149
150
151
152
# File 'lib/bio/command.rb', line 145

def query_command(cmd, query = nil)
  case RUBY_PLATFORM
  when /mswin32|bccwin32/
    query_command_popen(cmd, query)
  else
    query_command_fork(cmd, query)
  end
end

.query_command_fork(cmd, query = nil) ⇒ Object

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_popen.



177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
# File 'lib/bio/command.rb', line 177

def query_command_fork(cmd, query = nil)
  IO.popen("-", "r+") do |io|
    if io then
      # parent
      io.sync = true
      io.print query if query
      io.close_write
      io.read
    else
      # child
      begin
        Kernel.exec(*cmd)
      rescue Errno::ENOENT, Errno::EACCES
        Process.exit!(127)
      rescue Exception
      end
      Process.exit!(1)
    end
  end
end

.query_command_open3(cmd, query = nil) ⇒ Object

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.

From the view point of security, this method is recommended rather than exec_local_popen.



204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
# File 'lib/bio/command.rb', line 204

def query_command_open3(cmd, query = nil)
  errorlog = nil
  cmd = cmd.collect { |x| x.to_s }
  Open3.popen3(*cmd) do |pin, pout, perr|
    perr.sync = true
    t = Thread.start { errorlog = perr.read }
    begin
      pin.print query if query
      pin.close
      output = pout.read
    ensure
      t.join
    end
    [ output, errorlog ]
  end
end

.query_command_popen(cmd, query = nil) ⇒ Object

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.



159
160
161
162
163
164
165
166
167
# File 'lib/bio/command.rb', line 159

def query_command_popen(cmd, query = nil)
  str = make_command_line(cmd)
  IO.popen(str, "w+") do |io|
    io.sync = true
    io.print query if query
    io.close_write
    io.read
  end
end

.read_uri(uri) ⇒ Object

Same as OpenURI.open_uri(uri).read.



222
223
224
# File 'lib/bio/command.rb', line 222

def read_uri(uri)
  OpenURI.open_uri(uri).read
end

.start_http(address, port = 80, &block) ⇒ Object

Same as:

Net::HTTP.start(address, port)

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



232
233
234
235
236
237
238
239
240
241
242
243
# File 'lib/bio/command.rb', line 232

def start_http(address, port = 80, &block)
  uri = URI.parse("http://#{address}:#{port}")
  # Note: URI#find_proxy is an unofficial method defined in open-uri.rb.
  # If the spec of open-uri.rb would be changed, we should change below.
  if proxyuri = uri.find_proxy then
    raise 'Non-HTTP proxy' if proxyuri.class != URI::HTTP
    http = Net::HTTP.Proxy(proxyuri.host, proxyuri.port)
  else
    http = Net::HTTP
  end
  http.start(address, port, &block)
end