Algunos comandos escriben advertencias insignificantes en stderr pero devuelven el estado de salida 0.

¿Hay alguna manera de verificar que el estado de salida del comando es 0 y stderr está vacío, sin crear archivos temporales y con una sola declaración?

0
vstepaniuk 27 abr. 2020 a las 23:22

4 respuestas

La mejor respuesta

Gracias @Cyrus y @chepner para los comentarios:

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

El stderr del command 2>&1 >/dev/null se redirige a donde estaba stdout antes, y el propio stdout se redirige a /dev/null. Luego, stderr se asigna a la variable stderr en stderr=$(command 2>&1 >/dev/null), y el código de salida de esta asignación es el de solo command, por lo que se utiliza en {{X9 }}. Luego, si el código de salida de command es cero (y solo en este caso), se verifica la segunda condición (test -z "$stderr"), que prueba si la variable $stderr es de longitud cero cuerda.

0
vstepaniuk 2 may. 2020 a las 07:11

Yo sugiero:

stderr=$(your_command 2>&1 >/dev/null)
[[ $? == 0 ]] && [[ ${#stderr} == 0 ]] && echo okay
3
Cyrus 27 abr. 2020 a las 21:01

La esperanza a continuación es útil.

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

Archivo correspondiente test.c en C

   #include <stdio.h>
   int main ( ) {
       printf( "hello " );
       fprintf( stderr, "HELP!" );
       printf( " world\n" );
       return 1;
   }
0
choppe 27 abr. 2020 a las 20:46

Debido a cómo funciona set -o pipefail, puede tomar stderr en una tubería y salir con un estado de salida distinto de cero de la tubería si hay algo en stderr. Entonces, básicamente, pipefail realiza el estado binario y de salida o salida: si el comando sale con un estado de salida distinto de cero, pipefail falla la tubería con este estado, si el comando sale con un estado de salida cero, entonces el derecho el lado de la tubería fallará con un estado de salida distinto de cero, por lo que toda la tubería saldrá con un estado de salida distinto de cero.

# 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
}

Probada contra todas las combinaciones:

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

Las salidas 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

Implementación alternativa y un poco más detallado y más consumo de memoria, pero más posix-ish que podría tomar stderr a una variable y verificar si no es cero:

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"
}
2
KamilCuk 27 abr. 2020 a las 21:09