【问题标题】:Why does bash eval return zero when backgrounded command fails?当后台命令失败时,为什么 bash eval 返回零?
【发布时间】:2015-10-14 21:34:44
【问题描述】:

编辑这篇文章,原文在底部的“谢谢!”下面

command='a.out arg1 arg2 &'
eval ${command}
if [ $? -ne 0 ]; then
   printf "Command \'${command}\' failed\n"
   exit 1
fi
wait

这是一个演示问题的测试脚本,我把它简化了 在原帖中。注意第 2 行中的 & 和 wait 命令。 这些更忠实地代表了我的剧本。以防万一,与号 有时存在有时不存在,它的存在由用户决定- 指定的标志,指示是否将长算术作为背景 计算。而且,如果这很重要,我实际上是在为许多人提供背景 (十二) 个进程,即${command[0..11]}。如果有的话,我希望脚本死掉 失败。我使用'wait'来同步所有进程的成功返回。 很高兴(有点)使用另一种方法,但这几乎有效。

& 符号(用于背景)似乎导致了问题。 当${command} 省略与号时,脚本将按预期运行: 未找到可执行文件 a.out,发出有关此内容的投诉, 和美元?非零,因此主机脚本退出。当${command} 包括 & 号,发出同样的投诉,但 $?是零,所以 脚本继续执行。我希望脚本在 a.out 失败,但我如何从 a 获取非零返回值 后台进程?

谢谢!

(原帖):

我有一个使用表单命令的 bash 脚本

eval ${command}
if [ $? -ne 0 ]; then
   printf "Command ${command} failed"
   exit 1
fi

其中 ${command} 是一串单词,例如“a.out arg1 ... argn”。 问题是 eval 的返回码(即$?)总是 即使${command} 失败,也为零。从上面删除“eval” sn-p 允许返回正确的返回码 ($?),因此 停止脚本。我需要将命令字符串保存在变量中 (${command}) 以便在其他地方操作它,并简单地运行 ${command} 没有 eval 由于其他原因无法正常工作。我如何赶上 使用 eval 时返回码正确吗?

谢谢! 查理

【问题讨论】:

  • 请创建一个minimal reproducible example 来演示该问题。对eval trueeval false 的快速检查显示$? 的行为符合预期。
  • 另外,我质疑eval 的必要性。你能准确地说明你为什么需要它吗?可能有一种方法可以在没有它的情况下做同样的事情。
  • (1) eval 确实返回命令的退出代码。如果您真的没有看到,您需要告诉我们${command} 中的内容。 (2) 好的 bash 代码几乎从不使用eval。如果你认为你需要eval,你可能想解释一下原因,看看这里的人是否可以提供更好的方法。
  • eval 的 Bash 手册说:eval [arguments] 将参数连接在一起形成一个命令,然后读取并执行该命令,其退出状态作为 eval 的退出状态返回.如果没有参数或只有空参数,则返回状态为零。 因此,您所看到的与手册所说的内容背道而驰。

标签: bash eval


【解决方案1】:

& 符号(用于背景)似乎导致了问题。

没错。

在命令完成之前,shell 无法知道命令的退出代码。当您将命令置于后台时,shell 不会等待 完成。因此,它无法知道后台命令的(未来)返回状态。

这记录在man bash:

如果一个命令被控制操作符 & 终止,shell 在子shell 的后台执行命令。外壳可以 不等待命令完成,返回状态为0。

换句话说,将命令置于后台后的返回码始终为 0,因为 shell 无法预测尚未完成的命令的未来返回码。

如果要在后台查看命令的返回状态,需要使用wait命令。

示例

命令false总是设置返回状态为1:

$ false ; echo status=$?
status=1

但请注意,如果我们将其作为背景会发生什么:

$ false & echo status=$?
[1] 4051
status=0

状态为 0,因为命令被置于后台,shell 无法预测其未来的退出代码。如果我们稍等片刻,我们将看到:

$ 
[1]+  Exit 1                  false

在这里,shell 正在通知我们背景任务已完成,其返回状态应为:1。

在上面,我们没有使用eval。如果我们这样做,什么都不会改变:

$ eval 'false &' ; echo status=$?
[1] 4094
status=0
$ 
[1]+  Exit 1                  false

如果您确实想要后台命令的返回状态,请使用等待。比如这里展示了如何捕获false的返回状态:

$ false & wait $!; echo status=$?
[1] 4613
[1]+  Exit 1                  false
status=1

【讨论】:

  • 但是command & wait $!不等于command而不将其设置为背景?
  • @stimur 是的。 command & wait $! 的目的是作为一种工具,用于教授 shell 如何处理在后台运行的命令。顺便说一句,一个类似但可能有用的案例是command & other_commands; wait $!
【解决方案2】:

来自我系统上的手册页:

eval [arg ...] args 被读取并连接到一个命令中。然后这个命令被 shell 读取并执行,它的退出状态作为 eval 的值返回。如果没有参数,或者只有空参数,eval 返回 0。

如果您的系统文档是“相同的”,那么很可能是您正在运行的任何命令都是问题所在,即“a.out”在退出时返回“0”而不是非零值。将适当的“退出返回码”添加到已编译的程序中。

您也可以尝试使用 $() 来“运行”您的二进制文件,而不是“评估”它...,即

STATUS=$(a.out var var var)

只要流中只有一个“命令”,那么 $?是“退出代码”;否则,$?是多命令“管道”中最后一个命令的返回码...

:)
戴尔

【讨论】:

    猜你喜欢
    • 2021-08-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-04-28
    • 2016-06-08
    • 2015-09-05
    相关资源
    最近更新 更多