我在检查多管命令链中的某个命令是否引发错误时遇到问题。通常这不难检查,但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[@]}
,它会正确返回所有管道的退出状态。
我知道我可以将错误的流绑定到stdout但是我必须使用启发式方法来检查procdata
中的元素是有效还是错误消息,并且我冒着获得误报的风险。我还可以将stdout传递给/dev/null
并仅捕获错误流并检查是否${#procdata[@]} -eq 0
。但我必须重复调用以获取实际数据,整个命令的时间成本很高(大约3-5s)。我不想两次打电话。或者我可以使用临时文件来写错误,但我宁愿这样做而没有创建/删除文件的开销。
任何想法如何让我在bash中工作?
由于
P.S:
$ echo $BASH_VERSION
4.2.37(1)-release
答案 0 :(得分: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"