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

发布于 2024-12-08 17:10:16 字数 1428 浏览 0 评论 0原文

考虑我有以下命令行: do-things arg1 arg2 | Progress-meter "Doing things...";,其中 progress-meter 是我想要实现的 bash 函数。它应该在运行 do-things arg1 arg2 之前或并行打印 Doing things... (因此,无论如何它都会在一开始就被打印),并记录 stdout+ do-things 命令的 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,等待完成并获取其退出状态。

使用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”。我想要同样的东西而不需要评估。

Consider I have following commandline: do-things arg1 arg2 | progress-meter "Doing things...";, where progress-meter is bash function I want to implement. It should print Doing things... before running do-things arg1 arg2 or in parallel (so, it will be printed anyway at the very beginning), and record stdout+stderr of do-things command, and check it's exit status. If exit status is 0, it should print [ OK ], otherwise it should print [FAIL] and dump recorded output.

Currently I have things done using progress-meter "Doing things..." "do-things arg1 arg2";, and evaluating second argument inside, which is clumsy and I don't like that and believe there is better solution.

The problem with pipe syntax is that I don't know how can I get do-things' exit status from inside the pipeline? $PIPESTATUS seems to be useful only after all commands in pipeline finished.

Maybe process substitution like progress-meter "Doing things..." <(do-things arg1 arg2); will be fine, but in this case I also don't know how can I get exit status of do-things.

I'll be happy to hear if there is some other neat syntax possible to achieve same task without escaping command to be executed like in my example.

I greatly hope for the help of community.

UPD1: As question seems not to be clear enough, I paraphrase it:

I want bash function that can be fed with command, that will execute in parallel to function, and bash function will receive it's stdout+stderr, wait for completion and get its exit status.

Example implementation using 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;
}

So this can be used as progress_meter "Do things..." "do-things arg1 arg2". I want the same without eval.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

断念 2024-12-15 17:10:16

为什么要评估东西?假设您有一个固定的 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"

Why eval things? Assuming you have one fixed argument to progress-meter, you can do something like:

#!/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.

and call it

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

If you insist on putting progress-meter in a pipeline, I'm afraid your best bet is something like

(do-things arg1 arg2 ; echo $?) | progress-meter "Doing stuff"
夕嗳→ 2024-12-15 17:10:16

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

I'm not sure I understand what exactly you're trying to achieve,
but you could check the pipefail option:

 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.

For example:

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

Edit: I just read your comment on the other post. Why don't you just handle the error?

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
风铃鹿 2024-12-15 17:10:16

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

  1. 修改您的 do-things 脚本,以便它报告其内容

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

Have your scrips in the pipeline communicate by proxy (much like the Blackboard Pattern: some guy writes on the blackboard, another guy reads it):

  1. Modify your do-things script so that it reports its exit status to a file somewhere.

  2. Modify your progress-meter script to read that file, using command line switches if you like so as not to hardcode the name of the blackboard file, for reporting the exit status of the program that it is reporting the progress for.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文