我在检查多管道命令链中的某个命令是否确实抛出错误时遇到问题。通常这不难检查,但也不难 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[@]}
总是 0
在 procdata=( $(eval $cmd) )
之后即使我有很多管道!?。然而,如果我直接在提示符下执行整个命令并调用 echo ${PIPESTATUS[@]}
紧接着,它会正确返回所有管道的退出状态。
我知道我可以将 err 流绑定(bind)到 stdout,但我必须使用启发式方法来检查 procdata
中的元素是否是有效或错误消息,我冒着误报的风险。我还可以将标准输出通过管道传输到 /dev/null
并仅捕获错误流并检查是否${#procdata[@]} -eq 0
.但是我必须重复调用以获取实际数据,并且整个命令非常耗时(大约 3-5 秒)。我不想叫它两次。或者我可以使用一个临时文件来写入错误,但我宁愿在没有创建/删除文件开销的情况下这样做。
关于如何在 bash 中完成这项工作有什么想法吗?
谢谢
附言:
$ echo $BASH_VERSION
4.2.37(1)-release
最佳答案
这里有很多东西:
(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"
关于Bash:检查多管道命令链的退出状态,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20564652/