SHExecutor

SHExecutor is a convenience wrapper for executing shell commands from with-in a ruby process.

It supports:

  • blocking indefinitely, on exit you can get status, stdout and stderr
  • blocking with timeout, on exit you can get status, stdout, stderr, timeout exception (then output streams are not available)
  • non-blocking, then you get handles to the output streams and the status of the forked process
  • replacement, then you get a replaced process and none of the above
  • redirecting of stderr and stdout, separately, to file, with the option to overwrite or append

It does not support:

  • streaming input to stdin

For any executor:

  • you can ask status, which will tell you "not executed", current status (e.g. run or sleep) and "no longer executing"
  • you can ask result, which gives you nil unless the process is no longer executing, in which case you get status (e.g. pid, exit code, sigints.)

For a full description of status, please see Process::Status

This gem is sponsored by Hetzner (Pty) Ltd - http://hetzner.co.za

Initialization options:

Required / optional:

  {
    :application_path                     # Path of the command to run
    :params                               # Parameters to pass the command
  }
  @@default_options = {
    :timeout => -1,                     # Seconds after which to raise Timeout::Error if not completed
    :protect_against_injection => true, # look for spaces in and tainted application path
    :stdout_path => nil,                # file to append stdout to
    :stderr_path => nil,                # file to append stderr to
    :append_stdout_path => true,        # if true, will append, otherwise will overwrite
    :append_stderr_path => true,        # if true, will append, otherwise will overwrite
    :replace => false,                  # replace the running process with the command
    :wait_for_completion => false,      # block until the command completes
    :timeout_sig_kill_retry => 500      # if timeout occurs, send TERM, and send signal 9 if still present after X ms
  }

Installation

Add this line to your application's Gemfile:

gem 'shexecutor'

And then execute:

$ bundle

Or install it yourself as:

$ gem install shexecutor

Module helpers

Blocking

result, stdout, stderr = ::SHExecutor::execute_blocking("/bin/ls", "/tmp/")

Blocking with timeout

result, stdout, stderr = ::SHExecutor::execute_and_timeout_after("/bin/sleep", "10", 2)

Non-blocking

thr, stdout_io, stderr_io = ::SHExecutor::execute_non_blocking("/bin/sleep", "20")

Executor API

Blocking

iut = SHExecutor::Executor.new({:wait_for_completion => true, :application_path => "/bin/echo", :params => ["hello world"]})
result = iut.execute
iut.flush
puts "After execution status is: #{iut.status}"
# "no longer executing"
puts "out: #{iut.stdout} err: #{iut.stderr}"
puts "#{result.pid} success? #{result.success?} with code #{result.exitstatus}"
puts "For more see: #{iut.result.methods}"

Blocking with timeout

iut = SHExecutor::Executor.new({:timeout => 1, :wait_for_completion => true, :application_path => "/bin/sleep", :params => ["2"]})
result = iut.execute
# Timeout::Error gets raised. The spawned process is killed with TERM, and then with signal 9 if it does not close in timeout_sig_kill_retry ms

Non-blocking

iut = SHExecutor::Executor.new({:wait_for_completion => false, :application_path => "/bin/sleep", :params => ["1"]})
stdout, stderr, thr = iut.execute
puts "Status: #{iut.status}"
puts "PID: #{thr.pid}"
# "run" or "sleep"
sleep 2
puts "Status: #{iut.status}"
# "no longer executing"

Replacing

iut = SHExecutor::Executor.new({:replace => true, :application_path => "/bin/echo", :params => ["Your process has been assimilated"]})
iut.execute

stdout and stderr

iut.flush
puts iut.stdout
puts iut.stderr

redirecting stdout and stderr

iut = SHExecutor::Executor.new({:wait_for_completion => true, :application_path => "/bin/echo", :params => ["this is stdoutin a file"], :stdout_path => "/tmp/mystdout", :stderr_path => "/tmp/mystderr", :append_stdout_path => false, :append_stderr_path => true})
iut.execute
iut.flush
puts iut.stdout
puts iut.stderr

Remember to call iut.flush in order to stream stdout and stderr to the Executor object.

Note: calling flush will block if execute was called non-blocking, so wait until the execute has finished to preserve non-blocking behaviour.

Please send feedback and comments to the author at:

Ernst van Graan [email protected]

Contributing

  1. Fork it ( https://github.com/[my-github-username]/executor/fork )
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Commit your changes (git commit -am 'Add some feature')
  4. Push to the branch (git push origin my-new-feature)
  5. Create a new Pull Request