【问题标题】:How to get exit status of piped command from inside the pipeline?如何从管道内部获取管道命令的退出状态?
【发布时间】:2011-10-07 09:42:03
【问题描述】:

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

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

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

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

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

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

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

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

使用evals 的示例实现:

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"。我想要没有 eval 也一样。

【问题讨论】:

  • 感谢 Emil 的修复,但使用“evil”而不是“eval”是故意的。

标签: bash shell process try-catch


【解决方案1】:

为什么是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"

【讨论】:

  • 不过,我错过了。当我尝试就地重定向和/或使用化合物和/或使用流程扩展时,就会出现问题。由于这些事情 eval 令人难以置信,因为它工作正常,但是当将它作为单个字符串传递时,逃避所有这些东西是地狱。
  • 使用了你的解决方案,它似乎是最好的。
【解决方案2】:

我不确定我是否理解您想要实现的具体目标, 但您可以检查 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

【讨论】:

    【解决方案3】:

    让您在管道中的脚本通过代理进行通信(很像黑板模式:有人在黑板上写字,另一个人读它):

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

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

    【讨论】:

    • do-things 不是我的脚本,不幸的是 - 它可以是像 mkdir -p ${MY_TEMP_DIR} 这样的通用命令
    猜你喜欢
    • 2014-02-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-09-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多