为什么Ruby中的system()调用的退出状态会随着重定向而改变?

时间:2016-01-20 07:03:22

标签: ruby segmentation-fault exit-code

我已经减少了我面对MWE的问题。任何人都可以确认这是一个错误还是我遗漏了一些不成熟的东西?

平台

  • Ruby 1.9.3p484(2013-11-22修订版43786)[x86_64-linux]
  • GNU bash,版本4.3.11(1)-release(x86_64-pc-linux-gnu)
  • Ubuntu 14.04.2 x86_64

第1步:创建一个名为demo.rb

的Ruby脚本
ret = system("./segfault")
print "Return value: ", ret, "\n"
print "Exit status: ", ($?.exitstatus) ? $?.exitstatus : "nil", "\n"
print "Status code: ", $?.to_i, "\n"
puts "-----------------------------------"
ret = system("./segfault 2>&1")
print "Return value: ", ret, "\n"
print "Exit status: ", ($?.exitstatus) ? $?.exitstatus : "nil", "\n"
print "Status code: ", $?.to_i, "\n"

第2步:创建名为segfault.c

的C程序,如下所示
#include <string.h>

int main()
{
    memset((char *)0x0, 1, 100);
    return 1;
}

第3步:编译C程序。

gcc segfault.c -o segfault

第4步:运行Ruby脚本。

$ ruby segfault.rb 
Return value: false
Exit status: nil
Status code: 139
-----------------------------------
sh: line 1:  8181 Segmentation fault      (core dumped) ./segfault 2>&1
Return value: false
Exit status: 139
Status code: 35584

我无法理解为什么退出状态和状态代码会随stderr重定向到stdout而更改。有没有人对这种行为有合理的解释?

1 个答案:

答案 0 :(得分:0)

您提供给系统的命令可以采用多种形式。如果它只是命令的路径,或者它是一个路径和一个参数数组,那么ruby forks和execs直接命令。

如果它更复杂,那么它执行它认为的标准shell并将命令传递给shell。

相关的原因在于exitstatus的文档中说:

  

返回stat的返回码的最低有效位8位。仅在exited?为真时才可用。

exited?的文档说

  

如果stat正常退出(例如使用exit()调用或完成程序),则返回true。

你的c二进制文件没有正常退出,所以在你的第一种情况下exited?为false并且exitstatus返回nil在第二种情况下,当你的二进制文件仍然以相同的方式失败时,ruby执行的实际进程是你的shell实例,它正常退出。

就$?。to_i而言,我得到的结果不同。低8位包含诸如信号是否被引发以及高8位是否包含退出状态的信息。

在你的2个案例中我得到11和35584(139 <8),但是你的ruby版本似乎表现得像bash并且在信号值上增加了128(segfault是信号11)。

无论哪种方式,差异的原因是命令是否通过shell执行:shell看到失败的进程并将其信号值转换为退出状态(存储在高8位),而直接执行则没有退出状态和错误信息是低8位。

另外,您可以使用传递给系统的选项重定向输出,例如

system("mycommand", err: :out)

在你做的时候将stderr重定向到stdout