Execute System Commands in Ruby
在Ruby中运行系统命令
There are several ways to invoke system commands in Ruby:
- Backquotes `` or
%x()
. - The
system
method; - The
File
class - 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