Как проверить, что обе команды состояния выхода равны нулю, а stderr пуст в одном выражении? - PullRequest
0 голосов
/ 27 апреля 2020

Некоторые команды записывают незначительные предупреждения в stderr, но возвращают состояние выхода 0.

Есть ли способ проверить, что состояние завершения команды равно 0 и stderr пусто, без создания временных файлов и с одним утверждением?

Ответы [ 4 ]

3 голосов
/ 27 апреля 2020

Я предлагаю:

stderr=$(your_command 2>&1 >/dev/null)
[[ $? == 0 ]] && [[ ${#stderr} == 0 ]] && echo okay
2 голосов
/ 27 апреля 2020

Из-за того, как работает set -o pipefail, вы можете захватить stderr в трубе и выйти с ненулевым статусом выхода из трубы, если на stderr есть что-то. Таким образом, в основном pipefail выполняет двоичное состояние и состояние входа или выхода - если команда завершается с ненулевым состоянием выхода, 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-i sh вы можете получить 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 голосов
/ 29 апреля 2020

Спасибо @ Cyrus и @ chepner за комментарии:

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

stderr из command 2>&1 >/dev/null перенаправлен туда, куда stdout был раньше, а stdout сам перенаправлен на /dev/null. Затем stderr присваивается переменной stderr в stderr=$(command 2>&1 >/dev/null), а код выхода этого присваивания - просто из command, поэтому он используется в if. Затем, если код выхода command равен нулю (и только в этом случае), проверяется второе условие (test -z "$stderr"), которое проверяет, является ли переменная $stderr строкой нулевой длины.

0 голосов
/ 27 апреля 2020

Надеюсь, что ниже полезно.

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

Соответствующий тест. c Файл в C

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