Hegwin.Me

Thoughts grows up by feeding itself with its own words.

Execute System Commands in Ruby

在Ruby中运行系统命令

There are several ways to invoke system commands in Ruby:

  1. Backquotes `` or %x().
  2. The system method;
  3. The File class
  4. The open3 lib

Backquotes and system methods

The difference between backquotes and %x() and system is that: system only returns true/false; while %x returns the stdout of the command when the command execution succeeds, and an empty string "" when the command fails.

For example:

r = `ls`
 => "Dockerfile\nGemfile\nGemfile.lock..."
r = `cat NoFile`
cat: NoFile: No such file or directory
 => "" 

system("ls")
Dockerfile Jenkinsfile bin
 => true 
system("cat NoFile")
cat: NoFile: No such file or directory
 => false 

So it seems that the backquotes or %x() work better. When do you need to use system? My experience is that when you need to execute an interactive console, system is more convenient.

`rails console`
# get blocked

system 'rails console'
[Patch] Preventing bunny channel leaks
Loading development environment (Rails 6.0.0)
[1] pry(main)> exit
 => true 

Open3

There is no way to get the STDERR of the command, either backquote or system, so you can use Ruby's Open3 standard library, of which Open3.capture3 is a very good way to use it.

require 'open3'
 => true 

# When everything goes right
stdout, stderr, status = Open3.capture3('ls')
 => ["Dockerfile\nGemfile\nGemfile.lock... \n", "", #<Process::Status: pid 60368 exit 0>] 
status
 => #<Process::Status: pid 60368 exit 0>
status.exitstatus
 => 0 

# When something goes wrong
stdout, stderr, status = Open3.capture3('cat NOTHING')
 => ["", "cat: NOTHING: No such file or directory\n", #<Process::Status: pid 60385 exit 1>] 
stderr
 => "cat: NOTHING: No such file or directory\n" 
status
 => #<Process::Status: pid 60385 exit 1> 

Exit Code and Run Result

In the shell there is a variable $? that marks the exit code of the last command:

$ ls -alt
total 328
-rw-r--r-- 1 xiao.wang staff 927B Sep 17 14:18 Dockerfile
...
$ echo $?
0

$ cat NOTHING
cat: NOTHING: No such file or directory
$ echo $?
1

By convention, the exit code is 0 when the command is executed successfully, and a number other than 0 when it fails.

There is no absolute standard for this number, but there are some conventions: FreeBSD agrees on a larger number of exit codes: http://www.freebsd.org/cgi/man.cgi?query=sysexits&sektion=3 GNU is relatively simple: http://www.gnu.org/software/libc/manual/html_node/Exit-Status.html

In Ruby, there is a global variable with the same name as in the shell $?

system('echo Hello')
Hello
 => true 
$?
 => #<Process::Status: pid 60633 exit 0> 
$? .exitstatus
 => 0 

It is also possible to load the English standard library to make this variable more readable:

require 'English'
 => true 
2.6.3 :020 > system('echo 1')
1
$CHILD_STATUS
 => #<Process::Status: pid 60659 exit 0>
$CHILD_STATUS.exitstatus.zero?
 => true 
< Back