Código de comando de salida de registro, similar al comando de tiempo

10

utilizando

time sleep 1

rendimientos:

$ time sleep 1

real    0m1.005s
user    0m0.001s
sys     0m0.001s

¿Hay algún comando que pueda usar para imprimir el código de salida sleepo el comando que quiera ejecutar?

Algo le gusta:

$ log-exit-code sleep 1

tal vez esto suficiente?

sleep 1 && echo "$?"
Alexander Mills
fuente
3
No, sleep 1 && echo $?imprimiría el código de la celda durmiente solo cuando sea cero ...
Satō Katsura
oh lol tienes razón
Alexander Mills
1
sleep 1 && echo "$?" || echo "$?"
jesse_b
2
Agregué una respuesta ya que todos los demás hasta ahora descartan el valor de retorno original, lo cual es malo (después de esas respuestas, $? Siempre es 0, ya que lo último que hacen es un disply, que siempre funciona. Todos se olvidaron de mantener el valor de retorno original y lo devuelve después de mostrarlo. Por lo tanto, solo se lo muestran a un observador, pero lo 'ocultan' para el resto del script, que no puede usar ahora "$?" para ver si ese comando importante regresa 0 o algo más ...)
Olivier Dulac
1
Además, todas las soluciones a continuación pueden implementar fácilmente esta funcionalidad. Por ejemploEXIT_CODE=$(tellexit command)
Kusalananda

Respuestas:

4

En mis pruebas hasta ahora esto ha funcionado:

command && echo "$?" || echo "$?"

Simplemente le dice que haga eco del código de salida si tiene éxito o si falla.

Como señaló Sato a continuación, esto es esencialmente lo mismo que:

command; echo "$?"

Una cosa que podría hacer que valga la pena el comando y / o el comando es algo como:

command && echo "Success! Exit Code: $?" || echo "Failure! Exit Code: $?"

Si necesita que su script actúe sobre el código de salida, como le preocupa a Olivier, no es un problema. Su guión podría verse más o menos así:

command
case "$?" in; 
    0) echo "Command exited with: 0"
       <command if good>
       ;;
    1) echo "Command exited with: 1"
        <command if bad>
        ;;
    255) echo "Command exited with: 255"  # for ssh maybe?
         <command if 255>
         ;;
    *) echo "Command exited with: >2"
        <command for other exit code>
        ;;
esac
jesse_b
fuente
13
Err, ¿cómo es eso mejor que solo command; echo $??
Satō Katsura
44
Supongo que no. command; echo $?Es una respuesta mucho mejor.
jesse_b
uhhh, parece que es la mejor opción, así que la elegí como respuesta, si alguien no está de acuerdo, lmk
Alexander Mills
@AlexanderMills Solo usted sabe cuál es la respuesta más adecuada a su pregunta :-)
Kusalananda
1
No me gusta esto: esto cambia el código de salida después command ; echo $?porque echo $?siempre tiene éxito y, por lo tanto, después de eso, ¿el nuevo valor de $? ahora es 0. Hay una manera de mantener el código de retorno original incluso después de mostrarlo, en caso de que este código de retorno original sea útil más tarde (por ejemplo: en un script, es importante verificar el código de retorno antes de decidir cómo continuar). Vea mi respuesta para una de varias soluciones (la mayoría de las respuestas aquí se pueden cambiar de la misma manera)
Olivier Dulac
11

cmd && echo "$?"no funcionaría ya que, por necesidad, solo imprimiría ceros ( echosolo se ejecutaría al completar con éxito el comando anterior).

Aquí hay una breve función de shell para ti:

tellexit () {
    "$@"

    local err="$?"
    printf 'exit code\t%d\n' "$err" >/dev/tty
    return "$err"
}

Esto imprime el código de salida del comando dado de manera similar a como lo hace el timecomando.

$ tellexit echo "hello world"
hello world
exit code       0

$ tellexit false
exit code       1

Al redirigir a printfto /dev/ttyen la función, aún podemos usar tellexitredirecciones sin obtener basura en nuestra salida estándar o secuencias de error:

$ tellexit bash -c 'echo hello; echo world >&2' >out 2>err
exit code       0
$ cat out
hello
$ cat err
world

Al guardar el código de salida en una variable, podemos devolverlo a la persona que llama:

$ tellexit false || echo 'failed'
exit code       1
failed

Una versión más elegante de la misma función también imprime la señal que eliminó el comando si el código de salida es mayor que 128 (lo que significa que terminó debido a una señal):

tellexit () {
    "$@"

    local err="$?"

    if [ "$err" -gt 128 ]; then
        printf 'exit code\t%d (%s)\n' "$err" "$(kill -l "$err")" >/dev/tty
    else
        printf 'exit code\t%d\n' "$err" >/dev/tty
    fi

    return "$err"
}

Pruebas:

$ tellexit sh -c 'kill $$'
exit code       143 (TERM)

$ tellexit sh -c 'kill -9 $$'
Killed
exit code       137 (KILL)

(La localcosa requiere ash/ pdksh/ bash/ zsh, o puede cambiarlo a lo typesetque algunos otros shells también entienden).

Kusalananda
fuente
genial, ¿cómo usarías tellexit, muéstralo en acción por favor?
Alexander Mills
1
@AlexanderMills Ver actualización.
Kusalananda
2
Debe mantener el valor de retorno original y devolverlo al final.
Olivier Dulac
@OlivierDulac Muy buena observación! Lo enmendaré.
Kusalananda
7

Use una función de envoltura de shell. Probablemente con un nombre diferente.

$ exito() { "$@"; echo $?; }
$ exito true
0
$ exito false
1
$ exito echo "test test"      
test test
0
$ 

(Por supuesto, esto corromperá la salida estándar, por lo tanto, use el ttyque se muestra en @Kusalananda o no lo use fuera de contextos interactivos).

Al desviarse hacia un territorio no portable, algunos shells pueden informar sobre el estado de todos los comandos en una tubería, no solo el último, por ejemplo, en ZSH si desea que se notifiquen fallas de una tubería completa:

% TRAPZERR() { print >/dev/tty $pipestatus }
% perl -e 'exit 42' | perl -e 'exit 99'
42 99
% false | perl -e 'exit 42' | perl -e 'exit 99'
1 42 99
% perl -e 'exit 42' | perl -e 'exit 99' | true
% 

TRAPZERR de lo contrario no se dispara cuando no hay error (en el principal "no hay noticias son buenas noticias").

thrig
fuente
1
Debe mantener el valor de retorno original y devolverlo al final. Mostrar (printf, echo) una cosa simple (no comienza con un '-', etc.) siempre funciona, y después de eso aparece "$?" ahora tiene el valor "0" como funcionaba la pantalla
Olivier Dulac
7

GNU timetiene una opción para esto:

time -f %x sleep 1
0

Pasa a través del código de salida 1 , a menos que sea eliminado por una señal 2 :

$ /usr/bin/time -f %x sleep 1; echo $?
0
0

$ /usr/bin/time -f %x sleep 2x; echo $?
sleep: invalid time interval ‘2x’
Try 'sleep --help' for more information.
1
1

$ /usr/bin/time -f %x sleep 60s; echo $? # Press ^C before a minute elapses
0
2

Si desea conocer / manejar la situación de interrupción de la señal, pase -vy seleccione el stderr para la cadena Command terminated by signal.


1 Gracias a Olivier Dulac por señalar que pasa el código de salida.
2 Además, gracias Stéphane Chazelas por señalar que el código de salida de la señal de muerte no pasa.

obispo
fuente
1
Interesante GNU solo pero interesante, ya que ese (probablemente) mantendrá el código de retorno original para que el resto del código lo siga (es decir, no lo reemplaza con "0" como lo hacen las otras respuestas)
Olivier Dulac
2
Sin 0embargo, regresa si se mata el comando.
Stéphane Chazelas
2
@bishop, estoy de acuerdo en que 0 en esos casos es menos que ideal. Pero tenga en cuenta que no hay un acuerdo general sobre qué informar cuando una señal mata a un comando. Un número de señal de retorno de 128+ es utilizado por algunos shells. También se ven cosas como 256+signumo 384+signum, o incluso signum/256o signum/256+0.5si se genera un núcleo (en realidad el estado devuelto por waitpid()dividido por 256). Algunos darían una representación textual (como siginto sigquit+core). Todavía vale la pena discutir sobre el grupo de noticias gnu.coreutils.bugs, estoy de acuerdo.
Stéphane Chazelas
3
(en realidad timeno es parte coreutils, es un paquete en sí mismo con su propia lista de correo de errores ([email protected]))
Stéphane Chazelas
1
@bishop, el system()de los awks tradicionales como el que todavía mantiene Brian Kernighan (el kin awk) o el nawkde Solaris, el awkde FreeBSD que se deriva de eso. awk 'BEGIN{print system("kill -s ABRT $$")}'salidas 0.523438cuando el tamaño de volcado del núcleo no está limitado. El comportamiento de gawkha cambiado recientemente, con un comportamiento variable con --traditionalo --posix(ver también el valor de retorno de close(cmd)para más variación)
Stéphane Chazelas
2

No me gustan todas las otras respuestas (aunque me gusta mucho su inteligencia): muestran el código de salida pero también lo CAMBIAN. (la parte que se muestra es verdadera, por lo tanto, después de esto, el código de retorno es 0)

Aquí hay una versión modificada:

do_and_tell () {
   "$@"
   returncode="$?"
   printf "Execution of : \n%s\n yelded a return code of: \n%s\n" "$*" "$returncode"
   return $returncode  # the interresting addition... keeps the commands return value.
}

## usage:

prompt$ do_and_tell true
Execution of : 
true
 yelded a return code of: 
0

prompt$ echo $?
0

prompt$ do_and_tell false
Execution of : 
false
 yelded a return code of: 
1

prompt$ echo $?
1
Olivier Dulac
fuente
en realidad, la respuesta de @bishop es la única que mantiene este valor, pero se basa en la versión de GNU de la timecual no siempre está disponible.
Olivier Dulac
2

Para ser un poco más robusto, envíe el estado de salida a un FD separado para que pueda manejarse independientemente de stdout / stderr:

exit_status() {
    "$@"
    rv=$?
    echo >&3 "$rv"
    return "$rv"
}

# exit status and stdout to stdout:
> exit_status echo "hello" 3>&1
hello
0

# exit_status to stdout, stdout to /dev/null
> exit_status echo "hello" 3>&1 1>/dev/null
0
> exit_status ehco "hello" 3>&1 1>/dev/null
ehco: command not found
127

# exit_status to stdout, stdout and stderr to /dev/null
> exit_status ehco "hello" 3>&1 &>/dev/null
127

Tenga en cuenta que tendrá que hacer algo con FD3 o dirá Bad file descriptor.

Esto podría configurarse aún más para proporcionar estas salidas a las entradas de otro programa haciendo que el otro programa escuche en más de un FD; podrías usar esto como una especie de Kibana de principios de los 90 :)

onwsk8r
fuente
Debe mantener el valor de retorno original y devolverlo al final. Mostrar (printf, echo) una cosa simple (no comienza con un '-', etc.) siempre funciona, y después de eso aparece "$?" ahora tiene el valor "0" como funcionaba la pantalla
Olivier Dulac
1

Si bien todas las otras respuestas (muy interesantes) abordan la pregunta exacta que hizo el OP, en la práctica, las cosas suelen ser más simples. Simplemente guarde el estado de salida en una variable (inmediatamente después del comando) e informe o inicie sesión de la forma que desee. Por ejemplo, imprimir en / dev / stderr o escribir / agregarlo como un mensaje de texto en un archivo de registro. Y también se conserva para su uso en la toma de decisiones / control de flujo en el código posterior.

Si está en una función:

function some-function () {
    local rc ## to avoid side effects
    ...
    some-command ## run any command
    rc=$?  ## save the return status in a variable
    echo "some-command returned $rc" ## optionally tell the user about it
    ...
    return $rc  ## at the end, if appropriate
}

Si está directamente en un script:

some-command ## run any command
rc=$?  ## save the return status in a variable
echo "some-command returned $rc" ## optionally tell the user about it
...
exit $rc    ## at the end to tell any calling script about it, if appropriate

(Agachándose y cubriéndose porque esto no responde la pregunta exacta ).

Joe
fuente