如何从管道内获取管道命令的退出状态?

时间:2011-10-06 21:21:44

标签: bash shell process try-catch

考虑我有以下命令行:do-things arg1 arg2 | progress-meter "Doing things...";,其中progress-meter是我要实现的bash函数。它应该在运行Doing things...之前打印do-things arg1 arg2或者并行打印(因此,它将在最开始打印),并记录do-things命令的stdout + stderr,并检查它的退出状态。如果退出状态为0,则应打印[ OK ],否则应打印[FAIL]并转储录制的输出。

目前我使用progress-meter "Doing things..." "do-things arg1 arg2";完成了一些事情,而eval使用了第二个参数,这是笨拙的,我不喜欢这样,并相信有更好的解决方案。

管道语法的问题是我不知道如何从管道中的内部获得do-things'退出状态? $PIPESTATUS似乎仅在管道中的所有命令完成后才有用。

也许像progress-meter "Doing things..." <(do-things arg1 arg2);这样的流程替换会很好,但在这种情况下,我也不知道如何才能获得do-things的退出状态。

我很高兴听到是否有其他简洁的语法可以实现相同的任务,而无需像我的例子那样转义执行命令。

我非常希望得到社区的帮助。

UPD1:由于问题似乎不够明确,我解释一下:

我想要bash函数可以使用命令,它将与函数并行执行,bash函数将接收它的stdout + stderr,等待完成并获得其退出状态。

使用eval的示例实现:

progress_meter() {
    local output;
    local errcode;

    echo -n -e $1;

    output=$( {
        eval "${cmd}";
    } 2>&1; );
    errcode=$?;

    if (( errcode )); then {
        echo '[FAIL]';
        echo "Output was: ${output}"
    } else {
        echo '[ OK ]';
    }; fi;
}

所以这可以用作progress_meter "Do things..." "do-things arg1 arg2"。我想要同样没有评估。

3 个答案:

答案 0 :(得分:2)

为什么eval事情?假设你有一个固定的progress-meter参数,你可以这样做:

#!/bin/bash
# progress meter
prompt="$1"
shift

echo "$prompt"

"$@"    # this just executes a command made up of 
        # arguments 2, 3, ... of the script
        # the real script should actually read its input, 
        # display progress meter etc.

并将其命名为

$ progress-meter "Doing stuff" do-things arg1 arg2

如果你坚持将progress-meter放入管道中,我担心你最好的选择是

(do-things arg1 arg2 ; echo $?) | progress-meter "Doing stuff"

答案 1 :(得分:1)

我不确定我明白你到底要做什么, 但你可以查看 pipefail 选项:

 pipefail
                              If  set,  the  return value of a pipeline is the
                              value of the last (rightmost)  command  to  exit
                              with  a non-zero status, or zero if all commands
                              in the pipeline exit successfully.  This  option
                              is disabled by default.

例如:

bash-4.1 $ ls no_such_a_file 2>&- | : && echo ok: $? || echo ko: $?
ok: 0
bash-4.1 $ set -o pipefail
bash-4.1 $ ls no_such_a_file 2>&- | : && echo ok: $? || echo ko: $?
ko: 2

编辑:我刚看完你对另一篇文章的评论。你为什么不处理错误?

bash-4.1 $ ls -d /tmp 2>&- || echo failed | while read; do [[ $REPLY == failed ]] && echo failed || echo "$REPLY"; done  
/tmp
bash-4.1 $ ls -d /tmpp 2>&- || echo failed | while read; do [[ $REPLY == failed ]] && echo failed || echo "$REPLY"; done  
failed

答案 2 :(得分:0)

让管道中的代表通过代理进行通信(很像Blackboard Pattern:有些人在黑板上写字,另一个人在读取它):

  1. 修改您的do-things脚本,以便将其退出状态报告给某个文件。

  2. 修改您的progress-meter脚本以读取该文件,如果您愿意,可以使用命令行开关来硬编码黑板文件的名称,以报告其报告的程序的退出状态的进展。