下面的Ruby方法有什么不同?

exec、system和%x()或反勾号

我知道它们被用来通过Ruby以编程方式执行终端命令,但我想知道为什么有三种不同的方式来实现这一点。


当前回答

系统

系统方法调用系统程序。必须将命令作为字符串参数提供给该方法。例如:

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

被调用的程序将使用Ruby程序的当前STDIN、STDOUT和STDERR对象。实际上,实际返回值是true, false或nil。在本例中,日期是通过STDIN的IO对象打印的。如果进程以零状态退出,该方法将返回true;如果进程以非零状态退出,则返回false;如果执行失败,则返回nil。

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

>> 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>

Backticks

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

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

全局变量$?也是通过反节拍设置的。对于反勾号,您还可以使用字符串插值。

%x()

使用%x可以替代反勾号样式。它还将返回输出。与它的亲戚%w和%q(以及其他)一样,只要括号样式的分隔符匹配,任何分隔符都可以满足要求。这意味着%x(日期),%x{日期}和%x日期-都是同义词。像反勾号%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

其他回答

他们做不同的事情。Exec将当前进程替换为新进程,并且永不返回。系统调用另一个进程并将其退出值返回给当前进程。使用反勾号调用另一个进程,并将该进程的输出返回给当前进程。

在我的案例中是这样的。

output = `nmap localhost`

这个函数将输出保存到变量中,所以答案是使用' '而不是system。

下面是基于这个答案的流程图。参见使用脚本模拟终端。

系统

系统方法调用系统程序。必须将命令作为字符串参数提供给该方法。例如:

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

被调用的程序将使用Ruby程序的当前STDIN、STDOUT和STDERR对象。实际上,实际返回值是true, false或nil。在本例中,日期是通过STDIN的IO对象打印的。如果进程以零状态退出,该方法将返回true;如果进程以非零状态退出,则返回false;如果执行失败,则返回nil。

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

>> 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>

Backticks

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

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

全局变量$?也是通过反节拍设置的。对于反勾号,您还可以使用字符串插值。

%x()

使用%x可以替代反勾号样式。它还将返回输出。与它的亲戚%w和%q(以及其他)一样,只要括号样式的分隔符匹配,任何分隔符都可以满足要求。这意味着%x(日期),%x{日期}和%x日期-都是同义词。像反勾号%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