Ruby, Difference between exec, system and %x() or Backticks
system
The system
method calls a system program. You have to provide the command as a string argument to this method. For example:
>> system("date")
Wed Sep 4 22:03:44 CEST 2013
=> true
The invoked program will use the current STDIN
, STDOUT
and STDERR
objects of your Ruby program. In fact, the actual return value is either true
, false
or nil
. In the example the date was printed through the IO object of STDIN
. The method will return true
if the process exited with a zero status, false
if the process exited with a non-zero status and nil
if the execution failed.
As of Ruby 2.6, passing exception: true
will raise an exception instead of returning false
or nil
:
>> system('invalid')
=> nil
>> system('invalid', exception: true)
Traceback (most recent call last):
...
Errno::ENOENT (No such file or directory - invalid)
Another side effect is that the global variable $?
is set to a Process::Status
object. This object will contain information about the call itself, including the process identifier (PID) of the invoked process and the exit status.
>> system("date")
Wed Sep 4 22:11:02 CEST 2013
=> true
>> $?
=> #<Process::Status: pid 15470 exit 0>
Backticks
Backticks (``) call a system program and return its output. As opposed to the first approach, the command is not provided through a string, but by putting it inside a backticks pair.
>> `date`
=> Wed Sep 4 22:22:51 CEST 2013
The global variable $?
is set through the backticks, too. With backticks you can also make use string interpolation.
%x()
Using %x
is an alternative to the backticks style. It will return the output, too. Like its relatives %w
and %q
(among others), any delimiter will suffice as long as bracket-style delimiters match. This means %x(date)
, %x{date}
and %x-date-
are all synonyms. Like backticks %x
can make use of string interpolation.
exec
By using Kernel#exec
the current process (your Ruby script) is replaced with the process invoked through exec
. The method can take a string as argument. In this case the string will be subject to shell expansion. When using more than one argument, then the first one is used to execute a program and the following are provided as arguments to the program to be invoked.
Open3.popen3
Sometimes the required information is written to standard input or standard error and you need to get control over those as well. Here Open3.popen3
comes in handy:
require 'open3'
Open3.popen3("curl http://example.com") do |stdin, stdout, stderr, thread|
pid = thread.pid
puts stdout.read.chomp
end
Non-blocking backticks
The problem with your code is that stdout.read
is a blocking call.
You could defer the reading until the command is finished.
At first, create the commands:
commands = Array.new(3) { |i| Open3.popen2("sleep 5; echo hello from #{i}") }
Then, wait for each command to finish:
commands.each { |stdin, stdout, wait_thr| wait_thr.join }
Finally, gather the output and close the IO streams:
commands.each do |stdin, stdout, wait_thr|
puts stdout.read
stdin.close
stdout.close
end
Output: (after 5 seconds)
hello from 0
hello from 1
hello from 2
The difference between :+ and &:+
&:+
is translated to a proc, while :+
is a Symbol. inject
supports receiving symbols, which is translated internally to a proc:
If you specify a block, then for each element in enum the block is
passed an accumulator value (memo) and the element. If you specify a
symbol instead, then each element in the collection will be passed to
the named method of memo. In either case, the result becomes the new
value for memo. At the end of the iteration, the final value of memo
is the return value for the method.
ruby backtick, kernel.system, exec , command does not succeed
Turns out the issue was I was not closing the files I was creating.
Apparently that was fine when I was invoking it directly from the shell or directly from .sh, because the ruby script had already exited.
However when I would invoke this from the Ruby script it would not be able to access the file and would create issues.
Bottom line: Close your files else loose half day of work !
Difference between x.inspect and x.to_s in Ruby?
They are usually but not always the same. According to the documentation for Object.inspect()
:
If not overridden, uses the to_s method to generate the string.
So by default, they return the same thing because inspect()
calls to_s()
. Sometimes, however, it makes sense to override to_s()
to do one thing, but when inspecting an object from irb, you want to see more details. So they can be set up to do different things.
When to use each method of launching a subprocess in Ruby
use backticks when you want to easily capture the output of a program in a variable. you probably only want to use this for short-running programs, because this will block.
system
is convenient in two different cases:a. You have a long running program and you want the output to print as it runs (e.g.
system("tar zxvf some_big_tarball.tar.gz")
)b.
system
can bypass the shell expansion likeexec
(compare the output ofsystem "echo *"
andsystem "echo", "*"
)system blocks until the subprocess has exited.
fork
has a couple different use cases as well:a. You want to run some ruby code in a separate process (e.g.
fork { .... }
b. You want to run a child process (or different program) without blocking progress of your script
fork { exec "bash" }
.fork
is your friend if you want to daemonize your program.IO.popen
is useful when you need to interact with the standard out and standard in of a program. Note that it doesn't capture standard err, so you need to redirect that with2>&1
if you care about that.popen3
gives you a separate file descriptor for standard error (for when you need to capture that separately from standard out)PTY.spawn
is necessary when you want the spawned program to behave like you are running from the terminal. See the difference ofgrep --color=auto pat file
when spawned withsystem
vsPTY.spawn
Getting output of system() calls in Ruby
I'd like to expand & clarify chaos's answer a bit.
If you surround your command with backticks, then you don't need to (explicitly) call system() at all. The backticks execute the command and return the output as a string. You can then assign the value to a variable like so:
output = `ls`
p output
or
printf output # escapes newline chars
Difference b/w exec( ) and system( ) in unix
exec
replaces your process with the specified program. Your program is done, and will not continue running.
spawn
starts a new process (probably by first using fork
), and runs the specified program while your program continues executing.
system
starts a new process (probably by first using fork
), and runs the specified program while your program waits. Once the child exits, your program continues.
Why isn't my backtick in Ruby working?
The backticks and %x variants return the output of the result in a variable. system()
will invoke the command and return !!return_code
.
1.9.3p327 :001 > `ifconfig`
=> "eth2: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>..."
1.9.3p327 :002 > %x(ifconfig)
=> "eth2: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>..."
1.9.3p327 :003 > system("ifconfig")
eth2: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
...
=> true
Related Topics
How to Call Shell Commands from Ruby
How to Create Multiple Submit Buttons For the Same Form in Rails
Pass Variables to Ruby Script Via Command Line
What Does 'Monkey Patching' Exactly Mean in Ruby
Why Are Gems Installed in a Directory With a Different Ruby Version Than I'M Running
How to Convert a Ruby Hash Object to Json
What Does ≪≪-Constant Do
What Do 'I' and '-I' in Regex Mean
Difference Between Include and Require in Ruby
In Ruby on Rails, How to Format a Date With the "Th" Suffix, as In, "Sun Oct 5Th"
What's the Difference Between a Proc and a Lambda in Ruby