Method: Cheetah.run

Defined in:
lib/cheetah.rb

.run(command, *args, options = {}) ⇒ Object .run(command_and_args, options = {}) ⇒ Object .run(*commands_and_args, options = {}) ⇒ Object

Runs external command(s) with specified arguments.

If the execution succeeds, the returned value depends on the value of the :stdout and :stderr options (see below). If the execution fails, the method raises an ExecutionFailed exception with detailed information about the failure. (In the single command case, the execution succeeds if the command can be executed and returns a zero exit status. In the multiple command case, the execution succeeds if the last command can be executed and returns a zero exit status.)

Commands and their arguments never undergo shell expansion - they are passed directly to the operating system. While this may create some inconvenience in certain cases, it eliminates a whole class of security bugs.

The execution can be logged using a logger passed in the :logger option. If a logger is set, the method will log the executed command(s), final exit status, passed input and both captured outputs (unless the :stdin, :stdout or :stderr option is set to an IO, which prevents logging the corresponding input or output).

The actual logging is handled by a separate object called recorder. By default, DefaultRecorder instance is used. It uses the Logger::INFO level for normal messages and the Logger::ERROR level for messages about errors (non-zero exit status or non-empty error output). If you need to customize the recording, you can create your own recorder (implementing the Recorder interface) and pass it in the :recorder option.

Values of options not set using the options parameter are taken from default_options. If a value is not specified there too, the default value described in the options parameter documentation is used.

Examples:

Run a command and capture its output

files = Cheetah.run("ls", "-la", stdout: :capture)

Run a command and capture its output into a stream

File.open("files.txt", "w") do |stdout|
  Cheetah.run("ls", "-la", stdout: stdout)
end

Run a command and handle errors

begin
  Cheetah.run("rm", "/etc/passwd")
rescue Cheetah::ExecutionFailed => e
  puts e.message
  puts "Standard output: #{e.stdout}"
  puts "Error ouptut:    #{e.stderr}"
end

Run a command with expected false and handle errors

begin
  # exit code 1 for grep mean not found
  result = Cheetah.run("grep", "userA", "/etc/passwd", allowed_exitstatus: 1)
  if result == 0
    puts "found"
  else
    puts "not found"
  end
rescue Cheetah::ExecutionFailed => e
  puts e.message
  puts "Standard output: #{e.stdout}"
  puts "Error ouptut:    #{e.stderr}"
end

more complex example with allowed_exitstatus

stdout, exitcode = Cheetah.run("cmd", stdout: :capture, allowed_exitstatus: 1..5)

Overloads:

  • .run(command, *args, options = {}) ⇒ Object

    Runs a command with its arguments specified separately.

    Examples:

    Cheetah.run("tar", "xzf", "foo.tar.gz")

    Parameters:

    • command (String)

      the command to execute

    • args (Array<String>)

      the command arguments

    • options (Hash) (defaults to: {})

      the options to execute the command with

    Options Hash (options):

    • :stdin (String, IO) — default: '') a `String` to use as command's standard input or an `IO` to read it from

      '') a String to use as command's standard input or an IO to read it from

    • :stdout (nil, :capture, IO) — default: nil

      specifies command's standard output handling

      • if set to nil, ignore the output
      • if set to :capture, capture the output and return it as a string (or as the first element of a two-element array of strings if the :stderr option is set to :capture too)
      • if set to an IO, write the ouptut into it gradually as the command produces it
    • :stderr (nil, :capture, IO) — default: nil

      specifies command's error output handling

      • if set to nil, ignore the output
      • if set to :capture, capture the output and return it as a string (or as the second element of a two-element array of strings if the :stdout option is set to :capture too)
      • if set to an IO, write the ouptut into it gradually as the command produces it
    • :logger (Logger, nil) — default: nil

      logger to log the command execution

    • :recorder (Recorder, nil) — default: DefaultRecorder.new

      recorder to handle the command execution logging

    • :allowed_exitstatus (Fixnum, .include?, nil) — default: nil

      Allows to specify allowed exit codes that do not cause exception. It adds as last element of result exitstatus.

    • :env (Hash) — default: {}

      Allows to update ENV for the time of running the command. if key maps to nil value it is deleted from ENV.

    • :chroot (String) — default: "/"

      Allows to run on different system root.

  • .run(command_and_args, options = {}) ⇒ Object

    Runs a command with its arguments specified together. This variant is useful mainly when building the command and its arguments programmatically.

    Examples:

    Cheetah.run(["tar", "xzf", "foo.tar.gz"])

    Parameters:

    • command_and_args (Array<String>)

      the command to execute (first element of the array) and its arguments (remaining elements)

    • options (Hash) (defaults to: {})

      the options to execute the command with, same as in the first variant

  • .run(*commands_and_args, options = {}) ⇒ Object

    Runs multiple commands piped togeter. Standard output of each command execpt the last one is connected to the standard input of the next command. Error outputs are aggregated together.

    Examples:

    processes = Cheetah.run(["ps", "aux"], ["grep", "ruby"], stdout: :capture)

    Parameters:

    • commands_and_args (Array<Array<String>>)

      the commands to execute as an array where each item is again an array containing an executed command in the first element and its arguments in the remaining ones

    • options (Hash) (defaults to: {})

      the options to execute the commands with, same as in the first variant

Raises:



383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
# File 'lib/cheetah.rb', line 383

def run(*args)
  options = args.last.is_a?(Hash) ? args.pop : {}
  options = BUILTIN_DEFAULT_OPTIONS.merge(@default_options).merge(options)

  options[:stdin] ||= "" # allow passing nil stdin see issue gh#11
  if !options[:allowed_exitstatus].respond_to?(:include?)
    options[:allowed_exitstatus] = Array(options[:allowed_exitstatus])
  end

  streamed = compute_streamed(options)
  streams  = build_streams(options, streamed)
  commands = build_commands(args)
  recorder = build_recorder(options)

  recorder.record_commands(commands)

  pid, pipes = fork_commands(commands, options)
  select_loop(streams, pipes, recorder)
  _pid, status = Process.wait2(pid)

  begin
    check_errors(commands, status, streams, streamed, options)
  ensure
    recorder.record_status(status)
  end

  build_result(streams, status, options)
end