如何在一条语句中同时检查命令的退出状态为零和stderr为空?

问题描述 投票:0回答:4

有些命令会把无关紧要的警告写到 stderr 但返回退出状态 0.

有没有办法检查命令的退出状态是否为------。0 stderr 是否为空,而不需要创建临时文件,只需要一条语句?

bash io-redirection stderr string-length error-code
4个回答
3
投票

我建议。

stderr=$(your_command 2>&1 >/dev/null)
[[ $? == 0 ]] && [[ ${#stderr} == 0 ]] && echo okay

2
投票

因为... set -o pipefail 工作,你可以在管道中抓取stderr,如果stderr上有什么东西,你可以从管道中以非零的退出状态退出。所以基本上 pipefail 做二进制和-ing或退出状态--如果命令退出时退出状态为非零。pipefail 以此状态使管道失败,如果命令以零退出状态退出,那么管道右侧将以非零退出状态失败,所以整个管道将以非零退出状态退出。

# Usage: exit_if_nonzero_or_stderr command [args...]
# Executes the command [args...] and 
# exits with nonzero exit status
# if the command exits with nonzero exit status
# __or__
# the command outputs anything on stderr.
exit_if_nonzero_or_stderr() {
        (       
                set -o pipefail
                { "$@" 1>&3 ;} 2>&1 | {
                        if IFS= read -r line; then
                                printf "%s\n" "$line"
                                cat 
                                exit 1
                        fi
                } >&2
        ) 3>&1
}

针对所有组合进行测试。

tester() { 
        for i in \
                'echo 1; true;' \
                'echo 1; false;' \
                'echo 1; echo 2 >&2; true;' \
                'echo 1; echo 2 >&2; false;'
        do
                eval "f() { $i }"
                set -o pipefail
                exit_if_nonzero_or_stderr f 2> >( \
                        sed 's/^/stderr: /' >&2) | 
                        sed 's/^/stdout: /';
                echo "f() { $i } -> exit status: $?";
        done
}
tester

tester 输出。

stdout: 1
f() { echo 1; true; } -> exit status: 0
stdout: 1
f() { echo 1; false; } -> exit status: 1
stderr: 2
stdout: 1
f() { echo 1; echo 2 >&2; true; } -> exit status: 1
stdout: 1
stderr: 2
f() { echo 1; echo 2 >&2; false; } -> exit status: 1

另一种实现方式,更啰嗦,更耗费内存,但更多的posix-ish你可以抓取 stderr 到一个变量,并检查它是否为非零。

exit_if_nonzero_or_stderr2() {
        local stderr ret
        # redirect stdout to output with temporary file descriptor
        # and grab stderr 
        { stderr=$({ "$@" 1>&3 ;} 2>&1 ) ;} 3>&1
        # remember return value
        ret=$?
        # if theres anything on stderr, output it
        if [[ -n "$stderr" ]]; then
                cat <<<"$stderr" >&2
                # if stderr, always nonzero
                return 1
        fi
        # else we return with the exit status of the command
        return "$ret"
}

0
投票

希望下面的内容对你有帮助。

 res=$(./test.out 2 >&1)
 if [ $? -eq 0 ];then
    stdout=$res
    echo $stdout
 else
    return_code=$?
    std_err=$res
    echo $stderr 
 fi

对应的C语言的test.c文件

   #include <stdio.h>
   int main ( ) {
       printf( "hello " );
       fprintf( stderr, "HELP!" );
       printf( " world\n" );
       return 1;
   }

0
投票

谢谢你 @Cyrus@chepner 的意见。

if stderr=$(command 2>&1 >/dev/null) && test -z "$stderr"; then echo "ok"; fi

The stderrcommand 2>&1 >/dev/null 被重定向到 stdout 以前是,而且 stdout 本身被重定向到 /dev/null.然后是 stderr 被分配给 stderr 中的变量 stderr=$(command 2>&1 >/dev/null),而这个任务的退出代码就是刚才的那个 command因此,它被用于 if 那么,如果退出代码为 command 为零(仅在这种情况下),第二个条件(test -z "$stderr")被选中--这是在测试是否有 $stderr 变量是一个零长度的字符串。

© www.soinside.com 2019 - 2024. All rights reserved.