2013-12-13 76 views
5

我在检查多管道命令链中的某个命令是否发生错误时遇到问题。通常这不难检查,但set -o pipefail和检查${PIPESTATUS[@]}都不适用于我的情况。设置是这样的:Bash:检查多管道命令链的退出状态

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是必要的,因为否则$snmpcmdinvalid option -- <grepoption>错误,是没有意义的,因为<grepoption>不是$snmpcmd选项明显抛出了。在这个阶段,我认为这是一个错误$snmpcmd,但这是另一个节目...

如果发生错误,procdata将为空。但是,由于两种不同的原因,它可能为空:要么是因为在执行$snmpcmd时发生错误(例如超时),要么是因为grep找不到要查找的内容。问题是,我需要能够区分这两种情况并分别处理它们。

因此,set -o pipefail不是一个选项,因为它会传播任何错误,我无法区分哪部分管道失败。另一方面echo ${PIPESTATUS[@]}总是0procdata=($(eval $cmd))即使我有很多管道!然而,如果我在提示符处直接执行整个命令,并立即调用echo ${PIPESTATUS[@]},它将正确返回所有管道的退出状态。

我知道我可以将err流绑定到标准输出,但我必须使用启发式方法来检查procdata中的元素是否有效或错误消息,并且我冒着获得误报的风险。我也可以将stdout管道输入到/dev/null并只捕获错误流并检查是否${#procdata[@]} -eq 0。但我不得不重复这个呼叫来获取实际数据,整个命令的时间成本很高(大约3-5s)。我不想打两次电话。或者我可以使用一个临时文件来写错误,但我宁愿这样做,而没有创建/删除文件的开销。

任何想法,我可以使这项工作在bash?

感谢

PS:

$ echo $BASH_VERSION 
4.2.37(1)-release 

回答

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" 
+0

我有同样的想法。不幸的是它不起作用,但我不知道为什么。我发布了确切的命令,因为我可能看不到明显的: – user3040975

+0

'local cmdargs =“ - CHf,-m $ mibs -v $ snmpver -c $ community $ agent”; local procdatacmd =“$ tblcmd $ cmdargs $ proc_table”; procdatacmd + =“| cut -d','-f $ fields | grep -we -e | sort | uniq -c | sed's/^ * \ | \”// g; s //,/ g' ; echo $ {PIPESTATUS [@]}“'然后执行:'declare -a procdata =($(head -n-1 <<< $ $ procdatacmd))'输出是空的......只是没有。 – user3040975

+0

@ user3040975你似乎没有_running_上面的行中有任何命令 – devnull