Execution of shell code in Ruby scripts
There are countless ways to execute shell code in Ruby. You should always use capture3
, for which we have a dedicated card because it is so good.
All other methods to execute shell commands have signficant downsides, such as:
- They do not tell you if the command has succeeded
- They only return STDOUT, but not STDERR with error messages
- They don't let you choose whether command output appears on the screen or not
- They allow to inject code that will be interpreted by the shell
- You always need to wait for a long-running process to finish before you can inspect or echo its output
Always use capture3
.
Deprecated ways to execute shell code in Ruby
This is just a reference for legacy code. For new code, always use capture3
.
%x{ } Archive or backticks – quick and easy
Returns the standard output of running the given command in a subshell. This is an alias for `...`, and you can use string interpolation.
Example:
^
name = 'ls'
result = which #{name}
It does not escape anything you inject in the string.
If you want to find out if the call was successful you can ask $?.success?
.
system Archive – when you want to know how it went
Similar to exec
, but executes the given command in a subshell. Your script will go on. Returns:
true
if the command gives zero exit statusfalse
for non zero exit status
Example:
^
if system 'cp', '/full/path/to/my_file', '/target/directory'
puts "You made it!"
else
puts "Something went wrong"
end
Note that system
(as well as exec
) has two distinct argument patterns:
-
a single string argument is passed to the shell like "Hey, lets pretend the user typed this. Please run it!"
Copy>> system 'echo *'
This is equivalent to:
Copy$ echo *
Note how the asterisk is interpreted by the shell, as would any other characters (like a semicolon which separates commands).
Output will be something like:
Copyfile1 file2 file3
-
when passed multiple arguments, Ruby will take the first as command and find it on the $PATH. It then invokes it, passing the remaining arguments as Strings:
Copysystem 'echo', '*'
This is equivalent to:
Copy$ /bin/echo '*'
Since Ruby is passing all arguments as Strings, there will be no wildcard expansion or the like.
Output will be:
Copy*
You cannot freely mix these styles, and system
will fail when passing it invalid arguments:
Copysystem 'bundle exec rails server', '-p 3000' # fails and returns nil
This is equivalent to running a command called "bundle exec rails
" (including spaces in its filename). There is usually no such command anywhere on the $PATH
.
Note that you should prefer the 2nd approach (list of arguments instead of putting them into a single command) unless you absolutely know what you are doing.
exec Archive – the last thing you do
Replaces the current process. Take care: when an exec
command exits, it exits your script as well; i.e. exec
will be the last statement executed in your Ruby script. Output is written to stdout
.
Raises SystemCallError
if the command couldn‘t execute.
Example:
^
puts "Creating directory..."
exec 'mkdir', 'new_directory'
# the code here and below will never be read
spawn Archive – gives you a good level of control
Takes the same parameters as exec
and system
: [env,] command [,options]
env
Optional hash with :name => environment_name
, e.g. spawn(:name => 'development', 'echo hello world')
command, either:
- A single string which is passed to the standard shell (with shell expansion), e.g.
exec('cat * | grep denied')
- The command name and one or more plain arguments (without shell expansion), e.g.
exec *[ 'git', 'add', 'Gemfile' ]
If the first command is a two-element array, the first element is taken as the command to be executed, and the second argument is used as the argv[0] value, which may show up in process listings. E.g.exec ['ls', 'foo'], 'foo.bar', 'baz.bar'
options
see the Ruby docs Archive
The shell used is /bin/sh
on Unix-like systems, ENV["RUBYSHELL"]
or ENV["COMSPEC"]
on Windows NT series, and similar.
Spawn
calls
IO.popen
Archive
.
makandra has been working exclusively with Ruby on Rails since 2007. Our laser focus on a single technology has made us a leader in this space.