Hegwin.Me

The bitterest tears shed over graves are for words left unsaid and deeds left undone.

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