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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/20564652/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-18 08:59:40  来源:igfitidea点击:

Bash: Checking for exit status of multi-pipe command chain

bashpipesnmpexit-codecommand-substitution

提问by user3040975

I have a problem checking whether a certain command in a multi-pipe command chain did throw an error. Usually this is not hard to check but neither set -o pipefailnor checking ${PIPESTATUS[@]}works in my case. The setup is like this:

我在检查多管道命令链中的某个命令是否确实引发了错误时遇到了问题。通常这并不难检查,但在我的情况下,检查set -o pipefail也不起作用${PIPESTATUS[@]}。设置是这样的:

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

Note-1: The command was tested thoroughly and works perfectly.

注 1:该命令经过彻底测试并且运行良好。

Now, I want to store the output of that command in an array called procdata. Thus, I did:

现在,我想将该命令的输出存储在一个名为procdata. 因此,我做了:

declare -a procdata
procdata=( $(eval $cmd) )

Note-2: evalis necessary because otherwise $snmpcmdthrows up with an invalid option -- <grepoption>error which makes no sense because <grepoption>is not an $snmpcmdoption obviously. At this stage I consider this a bug with $snmpcmdbut that's another show...

注2:eval是必要的,否则$snmpcmd会抛出一个invalid option -- <grepoption>没有意义的错误,因为<grepoption>$snmpcmd显然不是一个选项。在这个阶段,我认为这是一个错误,$snmpcmd但那是另一个节目......

If an error occurres, procdatawill be empty. However, it might be empty for two different reasons: either because an error occurred while executing the $snmpcmd(e.g. timeout) or because grepcouldn't find what it was looking for. The problem is, I need to be able to distinguish between these two cases and handle them separately.

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

Thus, set -o pipefailis not an option since it will propagate any error and I can't distinguish which part of the pipe failed. On the other hand echo ${PIPESTATUS[@]}is always 0after procdata=( $(eval $cmd) )even though I have many pipes!?. Yet if I execute the whole command directly at the prompt and call echo ${PIPESTATUS[@]}immediately after, it returns the exit status of all the pipes correctly.

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

I know I could bind the err stream to stdout but I would have to use heuristic methods to check whether the elements in procdataare valid or error messages and I run the risk of getting false positives. I could also pipe stdout to /dev/nulland capture only the error stream and check whether ${#procdata[@]} -eq 0. But I'd have to repeat the call to get the actual data and the whole command is time costly (ca. 3-5s). I wouldn't want to call it twice. Or I could use a temporary file to write errors to but I'd rather do it without the overhead of creating/deleting files.

我知道我可以将 err 流绑定到 stdout,但我必须使用启发式方法来检查元素procdata是否有效或错误消息,并且我冒着误报的风险。我还可以将标准输出通过管道传输到/dev/null并仅捕获错误流并检查${#procdata[@]} -eq 0. 但是我必须重复调用才能获取实际数据,并且整个命令非常耗时(大约 3-5 秒)。我不想叫它两次。或者我可以使用一个临时文件来写入错误,但我宁愿这样做没有创建/删除文件的开销。

Any ideas how I can make this work in bash?

有什么想法可以让我在 bash 中工作吗?

Thanks

谢谢

P.S.:

PS:

$ echo $BASH_VERSION
4.2.37(1)-release

采纳答案by devnull

A number of things here:

这里有很多事情:

(1) When you say eval $cmdand attempt to get the exit values of the processes in the pipeline contained in the command $cmd, echo "${PIPESTATUS[@]}"would contain onlythe exit status for eval. Instead of eval, you'd need to supply the complete command line.

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

(2) You need to get the PIPESTATUSwhile assigning the output of the pipeline to the variable. Attempting to do that later wouldn't work.

(2) 需要获取PIPESTATUSwhile将管道的输出赋值给变量。稍后再尝试这样做是行不通的。



As an example, you can say:

例如,你可以说:

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

This captures the output of the pipeline and the PIPESTATUSarray into the variable foo.

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

You could get the command output into an array by saying:

您可以通过以下方式将命令输出放入数组中:

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

and the PIPESTATUSarray by saying

PIPESTATUS数组说

tail -1 <<< "$foo"