Ruby,exec、system和%x()或反引号之间的区别

425

以下 Ruby 方法有什么不同之处?

execsystem%x() 或称为反引号

我知道它们都可以通过 Ruby 在程序中执行终端命令,但我想知道为什么会有三种不同的方法。


1
这些命令和许多其他命令在文档中有很好的解释:exec system backticks - zetetic
1
有一篇关于该主题的很棒的 Ruby Quicktips 文章:执行 shell 命令 - Simon Perepelitsa
6
有人挖出了这个旧帖子,"Working With Unix Processes" 是一本对此感兴趣的 Ruby 开发者非常棒的书籍。链接为 http://workingwithunixprocesses.com/。 - Michael Kohl
1
我很惊讶没有任何答案提到 sh - Dennis
@Dennis 当我提出这个问题时,Ruby 1.9.3*还没有发布。 - Mr. Black
@Dennis sh将一个shell(可能是$SHELL)包装在你运行的任何内容周围。有人能解释一下这什么时候有用/必要吗? :) - Simon B.
4个回答

474

系统

system 方法调用一个系统程序。你需要将命令作为字符串参数提供给该方法。例如:

>> system("date")
Wed Sep 4 22:03:44 CEST 2013
=> true

被调用的程序将使用您的Ruby程序当前的STDINSTDOUTSTDERR对象。实际的返回值只能是truefalsenil。在示例中,日期是通过STDIN的IO对象打印出来的。如果进程以零状态退出,则该方法将返回true;如果进程以非零状态退出,则返回false;如果执行失败,则返回nil

从Ruby 2.6开始,传递exception: true将引发异常而不是返回falsenil

>> system('invalid')
=> nil

>> system('invalid', exception: true)
Traceback (most recent call last):
...
Errno::ENOENT (No such file or directory - invalid)

另一个副作用是,全局变量$?被设置为一个Process::Status对象。该对象将包含有关调用本身的信息,包括所调用进程的进程标识符(PID)和退出状态。

>> system("date")
Wed Sep 4 22:11:02 CEST 2013
=> true
>> $?
=> #<Process::Status: pid 15470 exit 0>

反引号

反引号(``)调用系统程序并返回其输出。与第一种方法不同的是,命令不是通过字符串提供的,而是将其放在反引号对中。

>> `date`
=> Wed Sep 4 22:22:51 CEST 2013   

全局变量$?也可以通过反引号进行设置。使用反引号时,您还可以使用字符串插值。

%x()

使用%x是反引号样式的替代方法。它也会返回输出结果。与其他相关的%w%q一样,只要括号样式的定界符匹配即可使用任何定界符。这意味着%x(date)%x{date}%x-date-都是同义词。与反引号一样,%x也可以使用字符串插值。

exec

通过使用Kernel#exec,当前进程(Ruby脚本)将被用exec调用的进程所替换。该方法可以接受一个字符串作为参数。在这种情况下,该字符串将遵循Shell扩展。当使用多个参数时,第一个参数用于执行程序,其余的参数则用作要调用的程序的参数。

Open3.popen3

有时需要将所需的信息写入标准输入或标准错误,您还需要控制它们。这时就可以使用Open3.popen3

require 'open3'

Open3.popen3("curl http://example.com") do |stdin, stdout, stderr, thread|
   pid = thread.pid
   puts stdout.read.chomp
end

4
为了更精细地控制调用处理 STDINSTDOUTSTDERR 的方式,考虑使用 Open3.popen3;可参考 https://dev59.com/cmgu5IYBdhLWcg3w_cBV#10922097。 - cboettig
1
感谢您提到反引号支持字符串插值,这解决了我的问题。 - adg

326
以下是关于 这个答案 的流程图,也可以参考使用 script 模拟终端的方法,请见 此处

图片描述


3
这并不是那么简单。在我的情况下,“阻塞直到进程完成”是“可以的(也需要的)”,然后使用popen3来检查STDOUT/STDERR输出。 - Nakilon
你可以通过将非阻塞调用放入 while 循环中来实现(有效地)阻塞。但是,将阻塞调用变成非阻塞调用并不容易。 - Ian
6
典型的StackOverflow——每个人都很挑剔,但说实话,这张流程图非常方便,对我帮助很大。谢谢。 - user2490003

119

这两者功能不同。exec替换当前进程为新的进程,并且 从不返回system调用另一个进程,并返回该进程的退出值给当前进程。使用反引号则会调用另一个进程,并将该进程的输出结果返回给当前进程。


0

在我的情况下,这个方法有效。

output = `nmap localhost`

这个方法将输出保存到变量中,因此答案是使用 `` 而不是 system。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接