在shell命令失败时引发异常?

8

我正在使用Ruby编写一些脚本,并需要通过shell命令与一些非Ruby代码进行接口。我知道至少有6种不同的方法可以从Ruby执行shell命令,但不幸的是,这些方法似乎都不能在shell命令失败时停止执行。

基本上,我正在寻找做以下等效操作的东西:

set -o errexit

我希望能够在 Bash 脚本中执行命令并在命令执行失败时(即检查返回值是否为非零)引发异常,异常信息可以使用 stderr。虽然这不难实现,但似乎应该已经有现成的选项了。我是否错过了某些选项呢?

4个回答

13

9
您可以使用 Ruby 的特殊变量之一。$?(类似于相同的 shell 脚本变量)。
`ls`
if $? == 0
  # Ok to go
else
  # Not ok
end

几乎每个程序在一切正常时都会将此变量设置为0。

6
为什么要使用 to_s$? 转换成字符串再与字符串 0 比较?if $? == 0 - glenn jackman
@Nakilon $?.zero? 抛出 NoMethodError。 - Mu-ik Jeon
@Mu-ikJeon,如果您还没有调用任何子进程,那么您将面临这个问题,因为$?从一开始就是nil - Nakilon
1
@Nakilon $? 是 Process::Status 类。 puts $?.class # Process::Status 该类没有 'zero?' 方法。 https://ruby-doc.org/core-2.2.0/Process/Status.html - Mu-ik Jeon
2
最好的检查方式是 if $?.success? https://ruby-doc.org/core-2.2.0/Process/Status.html#method-i-success-3F。我使用的方式是 exit $?.to_i unless $?.success? - BvdBijl
显示剩余3条评论

9

最简单的方法是创建一个新函数(或重新定义现有函数)来调用system()并检查错误代码。

像这样:

old_sys = system

def system(...)
  old_system(...)
  if $? != 0 then raise :some_exception
end

这应该可以满足你的需求。

2
是的,这就是我在说“不会太难写”的时候所想的,但这似乎是应该已经解决了的问题,你知道吗?谢谢你提供的例子。 - Benjamin Oakes
1
虽然,你应该使用 raise 而不是 throw - glenn jackman
FYI:$? 的可读别名是 $CHILD_STATUS。参考文献:https://docs.ruby-lang.org/en/2.2.0/Process/Status.html,https://ruby-doc.org/stdlib-2.5.0/libdoc/English/rdoc/English.html - Ben Amos

8
稍微简化一下:您不需要检查 $? 的值,只需要运行的命令会输出到 stderr 中,通常情况下,您只需使用非零退出即可,而不是引发带有丑陋堆栈跟踪的异常。
system("<command>") || exit(1)

所以你可以再进一步,做到:

(system("<command 1>") &&
  system("<command 2>") &&
  system("<command 3>")) || exit(1)

...which would short-circuit and fail on error (in addition to being hard to read).

参考资料:从Ruby 2.0文档中的system(虽然1.8.7也是如此):

如果命令返回零退出状态,则system返回true,否则返回false

http://www.ruby-doc.org/core-2.0.0/Kernel.html#method-i-system


有趣的,我喜欢这个。你可以加一个链接到你的参考文献吗?(即你在 Ruby 文档中找到此内容的那一部分) - Benjamin Oakes

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