Bash:检查多管道命令链的退出状态

5

我遇到了一个问题,需要检查多管道命令链中是否有某个命令抛出了错误。通常这并不难检查,但在我的情况下,set -o pipefail和检查${PIPESTATUS[@]}都无法生效。我的设置如下:

cmd="$snmpcmd $snmpargs $agent $oid | grep <grepoptions> for_stuff | cut -d',' f$fields | sed 's/ubstitute/some_other_stuff/g'"

注意1:该命令已经经过充分测试,完美运行。
现在,我想把该命令的输出存储在一个名为procdata的数组中。因此,我执行了以下操作:
declare -a procdata
procdata=( $(eval $cmd) )

注意2:使用eval是必要的,否则$snmpcmd会出现一个invalid option -- <grepoption>错误,这毫无意义,因为<grepoption>显然不是$snmpcmd的选项。在这个阶段,我认为这是$snmpcmd的一个错误,但这又是另外一回事...。
如果发生错误,procdata将为空。然而,它可能由于两个不同的原因而为空:要么是因为执行$snmpcmd时出错了(例如超时),要么是因为grep找不到它要查找的内容。问题是,我需要能够区分这两种情况并单独处理它们。
因此,set -o pipefail不是一个选项,因为它会传播任何错误,并且我无法区分管道的哪一部分失败了。另一方面,即使我有许多管道,echo ${PIPESTATUS[@]}在执行procdata=( $(eval $cmd) )之后总是返回0!?。但是,如果我直接在提示符处执行整个命令,然后立即调用echo ${PIPESTATUS[@]},它会正确地返回所有管道的退出状态。
我知道我可以将错误流绑定到标准输出,但我必须使用启发式方法来检查procdata中的元素是有效还是错误消息,并且我冒着得到错误结果的风险。我也可以将标准输出导向/dev/null并仅捕获错误流,然后检查${#procdata[@]} -eq 0是否成立。但我需要重复调用来获取实际数据,并且整个命令时间成本很高(约3-5秒)。我不想调用两次。或者我可以使用一个临时文件来写入错误信息,但我宁愿不使用创建/删除文件的开销。
有什么办法可以使这在bash中工作吗?
谢谢。
$ echo $BASH_VERSION
4.2.37(1)-release
1个回答

3

以下是需要注意的几点:

(1) 当您使用eval $cmd并尝试获取命令$cmd中包含的管道中进程的退出值时,echo "${PIPESTATUS[@]}"包含eval的退出状态。您需要提供完整的命令行,而不是eval

(2) 您需要在将管道输出分配给变量时获取PIPESTATUS。稍后尝试这样做将无效。


例如,您可以这样说:

foo=$(command | grep something | command2; echo "${PIPESTATUS[@]})"

这段代码将管道的输出和PIPESTATUS数组捕获到变量foo中。

你可以通过以下命令将命令输出获取到一个数组中:

result=($(head -n -1 <<< "$foo"))

通过以下方式使用PIPESTATUS数组

tail -1 <<< "$foo"

我有同样的想法。不幸的是,它也不起作用,但我不确定为什么。我发布确切的命令,因为我可能看不到明显的问题: - user3040975
本地变量 cmdargs 赋值为 "-CHf, -m$mibs -v$snmpver -c$community $agent";本地变量 procdatacmd 赋值为 "$tblcmd $cmdargs $proc_table ";接着,将 "<proc1> -e <proc2>" 的结果通过管道符号(|)传递给 procdatacmd 变量进行处理。处理过程中使用了 cut 命令和 grep 命令,并对结果进行了排序、去重和格式转换,最后用 sed 命令进行替换。然后使用 ${PIPESTATUS[@]} 命令打印出了处理的状态码。接下来使用 declare -a procdata=( $(head -n -1 <<< $procdatacmd) ) 命令将处理后的数据存储到数组 procdata 中,但是输出为空,什么也没有。<proc1> 和 <proc2> 在代理上运行正常。 - user3040975
是的,我指的是变量。temp=$($tblcmd $cmdargs $proc_table | cut -d',' -f$fields | $( get_pgreplist ) | sort | uniq -c | sed 's/^ *\|\"/ /g;s/ /,/g' ; echo ${PIPESTATUS[@]})。现在 procdata=( $(head -n -1 <<< $temp) ) 没有输出任何内容,但是 procdata=( $(head <<< $temp) ) 逐行输出正确的值?!我很困惑... - user3040975
@user3040975 你似乎做错了什么。 1 0 1 0 0 0 表示已经捕获到了退出代码。只需要输入 echo "${temp}" 来查看它返回了什么。 - devnull
1
它按照你说的方式工作。我不知道我在上面做错了什么。如果我按照你在答案中所说的做,它就能工作。感谢你的时间和努力。只是为了完整起见:echo "${temp}" 输出 10,httpd,runnable 64,nfsd,runnable 0 0 0 0 0 0。搜索 http 输出 0 0 1 0 0 0,表示无法匹配,而虚假的 IP 将给出预期的 1 0 1 0 0 0。再次感谢!可惜我不能投票支持,因为声望不够... - user3040975
显示剩余5条评论

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