当使用非零退出代码的 shelled-out 命令 returns 时,如何使 Ruby 脚本失败?

How to get Ruby script to fail when shelled-out command returns with non-zero exit code?

在一个Ruby脚本中,有various ways调用系统命令/命令行

  1. 反引号:`command arg1 arg2`
  2. 分隔形式,例如%x(command arg1 arg2)(可用其他分隔符)
  3. Kernel#system方法:system('command arg1 arg2')
  4. Kernel#exec方法:exec('command arg1 arg2')

如果我希望 Ruby 脚本在被调用的命令失败时失败(有异常)(具有非零退出代码),我可以检查特殊变量中的退出代码 $? 对于前两个变体:

`command arg1 arg2`
fail unless $? == 0

%x,command arg1 arg2,
fail unless $? == 0

如果我同意将命令的标准输出转到 Ruby 脚本的标准输出(我也是),我可以使用变体 3 并检查其 return 值:

unless system('command arg1 arg2')
  fail
end

如果我不关心挽救异常的能力,也不关心未挽救异常的堆栈跟踪打印行为,我当然可以使用 exit(1) 或在前两个变体中 exit($?) fail.

的位置

如果进一步执行命令是 Ruby 脚本应该做的最后一件事,即使命令成功(退出代码 0),我可以使用第四种变体:

exec('command arg1 arg2')

这将用调用命令创建的新进程替换 Ruby 进程,但是 Ruby 脚本的调用者的效果是相同的:他看到一个非零如果调用的命令导致非零退出代码,则退出代码。

我非常喜欢第四种变体的简洁性,但如果执行命令不是万一成功的最后一件事,我很遗憾不能使用它。相比之下,其他变体的条件 failexit 调用看起来非常不干净,在我的一个用例中,经常违反单一抽象级别和单一责任原则。

我当然可以轻松地为前三种方法中的任何一种编写包装函数,使它们的用法看起来同样简洁,但由于这看起来像是一种基本的操作方式,我想知道是否 Ruby 已经内置了类似的东西 ... 可以是我可以使用的实用函数而不是我自己的包装器,或者是一种改变一个或多个命令调用行为的机制命令失败时导致错误或非零退出的方法,类似于 's and 's option to set -e.

据我所知,没有内置的方法可以做到这一点,但你几乎可以通过一点元编程魔法获得你想要的行为

def set_exit enable
  if enable
    define_method :system do |*args|
      Kernel.system *args
      exit $?.exitstatus unless $?.success?
    end
  else
    define_method :system, Kernel.instance_method(:system)
  end
end

set_exit true
# ...
# any failed system calls here will cause your script to exit
# ...
set_exit false
# now system is back to normal

这通过为 Object 重新定义 system 来实现,同时在需要内置行为时显式使用 Kernel.system