[ruby] Getting output of system() calls in Ruby

The straightforward way to do this correctly and securely is to use Open3.capture2(), Open3.capture2e(), or Open3.capture3().

Using ruby's backticks and its %x alias are NOT SECURE UNDER ANY CIRCUMSTANCES if used with untrusted data. It is DANGEROUS, plain and simple:

untrusted = "; date; echo"
out = `echo #{untrusted}`                              # BAD

untrusted = '"; date; echo"'
out = `echo "#{untrusted}"`                            # BAD

untrusted = "'; date; echo'"
out = `echo '#{untrusted}'`                            # BAD

The system function, in contrast, escapes arguments properly if used correctly:

ret = system "echo #{untrusted}"                       # BAD
ret = system 'echo', untrusted                         # good

Trouble is, it returns the exit code instead of the output, and capturing the latter is convoluted and messy.

The best answer in this thread so far mentions Open3, but not the functions that are best suited for the task. Open3.capture2, capture2e and capture3 work like system, but returns two or three arguments:

out, err, st = Open3.capture3("echo #{untrusted}")     # BAD
out, err, st = Open3.capture3('echo', untrusted)       # good
out_err, st  = Open3.capture2e('echo', untrusted)      # good
out, st      = Open3.capture2('echo', untrusted)       # good
p st.exitstatus

Another mentions IO.popen(). The syntax can be clumsy in the sense that it wants an array as input, but it works too:

out = IO.popen(['echo', untrusted]).read               # good

For convenience, you can wrap Open3.capture3() in a function, e.g.:

#
# Returns stdout on success, false on failure, nil on error
#
def syscall(*cmd)
  begin
    stdout, stderr, status = Open3.capture3(*cmd)
    status.success? && stdout.slice!(0..-(1 + $/.size)) # strip trailing eol
  rescue
  end
end

Example:

p system('foo')
p syscall('foo')
p system('which', 'foo')
p syscall('which', 'foo')
p system('which', 'which')
p syscall('which', 'which')

Yields the following:

nil
nil
false
false
/usr/bin/which         <— stdout from system('which', 'which')
true                   <- p system('which', 'which')
"/usr/bin/which"       <- p syscall('which', 'which')

Examples related to ruby

Uninitialized Constant MessagesController Embed ruby within URL : Middleman Blog Titlecase all entries into a form_for text field Ruby - ignore "exit" in code Empty brackets '[]' appearing when using .where find_spec_for_exe': can't find gem bundler (>= 0.a) (Gem::GemNotFoundException) How to update Ruby Version 2.0.0 to the latest version in Mac OSX Yosemite? How to fix "Your Ruby version is 2.3.0, but your Gemfile specified 2.2.5" while server starting Is the server running on host "localhost" (::1) and accepting TCP/IP connections on port 5432? How to update Ruby with Homebrew?

Examples related to system

cannot convert 'std::basic_string<char>' to 'const char*' for argument '1' to 'int system(const char*)' How to code a very simple login system with java Convert R vector to string vector of 1 element How to run cron once, daily at 10pm Java system properties and environment variables A terminal command for a rooted Android to remount /System as read/write Difference between subprocess.Popen and os.system How can I store the result of a system command in a Perl variable? Adding system header search path to Xcode How can I check the system version of Android?

Examples related to call

ReactJS - Call One Component Method From Another Component Call a Class From another class What does it mean to "call" a function in Python? How to make method call another one in classes? How to call a mysql stored procedure, with arguments, from command line? Submit form on pressing Enter with AngularJS Running bash script from within python javascript: get a function's variable's value within another function Passing variables, creating instances, self, The mechanics and usage of classes: need explanation Android Call an method from another class