Hegwin.Me

无边落木萧萧下,不尽长江滚滚来。

在Ruby中运行系统命令

在Ruby中有若干种方法来调用系统命令:

  1. 反引号``或者 %x();
  2. system 方法;
  3. File
  4. Open3

反引号和system方法

反引号及 %x()system 的区别在于: system 只会返回 true/false;而%x在命令执行成功时,会返回命令的stdout,当命令支持失败时,会返回空字符串 ""

举例来说:

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 

这样看来其实反引号或者 %x() 比较好用,但什么时候需要用到 system 呢?我的经验是当你需要执行一个interactive console的时候,system 会比较便利。

`rails console`
# get blocked

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

Open3

无论是反引号还是system,都没有办法拿到命令的STDERR,这时候就可以用Ruby的 Open3 标准库,其中 Open3.capture3 是一个非常好用的方法。

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与运行结果

在shell中有一个变量 $?,标记了上一次命令的exit code:

$ 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

就约定而言,当命令执行成功时,exis code为0;当执行失败时,exit 为0以外的数字。

这个数字并没有绝对的标准,不过还是有一些约定:
FreeBSD约定了比较多的exit code: http://www.freebsd.org/cgi/man.cgi?query=sysexits&sektion=3
GNU就相对简单: http://www.gnu.org/software/libc/manual/html_node/Exit-Status.html

在Ruby中,有一个和shell中同名的全局变量 $?

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

也可以加载English标准库,让这个变量有更好的可读性:

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

Tags:

Posted on: 2019-12-11

Back