Registra il codice di uscita del comando, simile al comando time


10

utilizzando

time sleep 1

rendimenti:

$ time sleep 1

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

c'è un comando che posso usare per stampare il codice di uscita sleepo qualunque comando io voglia eseguire?

Qualcosa di simile:

$ log-exit-code sleep 1

forse questo è sufficiente?

sleep 1 && echo "$?"

3
No, sleep 1 && echo $?stampa il codice della cella dormiente solo quando è zero ...
Satō Katsura,

oh lol hai ragione
Alexander Mills,

1
sleep 1 && echo "$?" || echo "$?"
jesse_b

2
Ho aggiunto una risposta dato che tutti gli altri finora scartano il valore di ritorno originale, che imo è male (dopo quelle risposte, $? È sempre 0 poiché l'ultima cosa che fanno è una disply, che funziona sempre. Si sono tutti dimenticati di mantenere il valore restituito originale e restituirlo dopo averlo visualizzato. Quindi lo mostrano solo a un osservatore, ma lo "nascondono" per il resto dello script, che non può usare ora "$?" per vedere se quel comando importante restituisce 0 o qualcos'altro ...)
Olivier Dulac l'

1
Anche tutte le soluzioni seguenti possono facilmente implementare questa funzionalità. Ad esempio Kusalananda sarebbe:EXIT_CODE=$(tellexit command)
jesse_b

Risposte:


4

Finora nei miei test ha funzionato:

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

Gli dice solo di fare eco al codice di uscita se ha successo o se fallisce.

Come ha sottolineato Sato, questo è essenzialmente lo stesso di:

command; echo "$?"

Una cosa che potrebbe rendere utile il comando e / o è qualcosa di simile:

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

Se hai bisogno che il tuo script agisca sul codice di uscita come è la preoccupazione di Olivier, non è un problema. La tua sceneggiatura potrebbe assomigliare a:

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

13
Err, come va meglio di solo command; echo $??
Satō Katsura,

4
Immagino di no. command; echo $?è una risposta molto migliore.
jesse_b

uhhh questa sembra essere l'opzione migliore, quindi l'ho scelta come risposta, se qualcuno non è d'accordo, lmk
Alexander Mills

@AlexanderMills Solo tu sai quale è la risposta più appropriata alla tua domanda :-)
Kusalananda

1
Non mi piace questo: questo cambia il codice di uscita dopo command ; echo $?perché ha echo $?sempre successo e quindi dopo di esso, il nuovo valore di $? è ora 0. Esiste un modo per mantenere il codice di ritorno originale anche dopo averlo visualizzato, nel caso in cui questo codice di ritorno originale sia utile in seguito (ad esempio: in uno script, è importante controllare il codice di ritorno prima di decidere come continuare). Vedi la mia risposta per una delle diverse soluzioni (la maggior parte delle risposte qui possono essere modificate allo stesso modo)
Olivier Dulac

11

cmd && echo "$?"non funzionerebbe poiché avrebbe necessariamente solo la stampa degli zeri (l' echoesecuzione sarebbe eseguita solo al completamento con successo del comando precedente).

Ecco una funzione di shell corta per te:

tellexit () {
    "$@"

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

Questo stampa il codice di uscita del comando dato in un modo simile a quello del timecomando.

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

$ tellexit false
exit code       1

Reindirizzando il printfto /dev/ttynella funzione, possiamo ancora utilizzare i tellexitreindirizzamenti senza ottenere spazzatura nei nostri output standard o flussi di errori:

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

Salvando il codice di uscita in una variabile siamo in grado di restituirlo al chiamante:

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

Una versione più elaborata della stessa funzione stampa anche il segnale che ha interrotto il comando se il codice di uscita è maggiore di 128 (il che significa che è terminato a causa di un segnale):

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

test:

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

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

(La localcosa richiede ash/ pdksh/ bash/ zsh, oppure puoi cambiarla in modo typesetche anche alcune altre shell capiscano.)


bene, come useresti Tellexit, mostralo in azione pls
Alexander Mills,

1
@AlexanderMills Vedi aggiornamento.
Kusalananda

2
Dovrebbe mantenere il valore restituito originale e restituirlo alla fine.
Olivier Dulac l'

@OlivierDulac Osservazione davvero buona! Lo modificherò.
Kusalananda

7

Usa una funzione wrapper shell. Probabilmente con un nome diverso.

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

(Questo ovviamente corromperà l'output standard, quindi usa o ttyquello mostrato da @Kusalananda o non usalo al di fuori dei contesti interattivi.)

Svoltando in un territorio non portabile, alcune shell possono riportare sullo stato di tutti i comandi in una pipeline, non solo l'ultimo, ad esempio in ZSH se si desidera che vengano segnalati guasti da un'intera pipeline:

% 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 altrimenti non si attiva quando non si verifica alcun errore (sul principio "nessuna notizia è una buona notizia").


1
Dovrebbe mantenere il valore restituito originale e restituirlo alla fine. La visualizzazione (printf, echo) di una cosa semplice (non inizia con un '-', ecc.) Funziona sempre, e dopo quella visualizzazione "$?" ora ha il valore "0" mentre il display funzionava
Olivier Dulac l'

7

GNU timeha un'opzione per questo:

time -f %x sleep 1
0

Passa attraverso il codice di uscita 1 , a meno che non venga ucciso da un segnale 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

Se vuoi conoscere / gestire la situazione di kill del segnale, passa -ve grep lo stderr per la stringa Command terminated by signal.


1 Grazie a Olivier Dulac per aver notato il codice di uscita che passa.
2 Inoltre, grazie a Stéphane Chazelas per aver segnalato il codice di uscita del segnale di interruzione non passa.


1
Interresting. Solo GNU ma interessante, poiché quello (probabilmente) manterrà il codice di ritorno originale per il resto del codice da seguire (cioè, non lo sostituirà con "0" come fanno le altre risposte)
Olivier Dulac

2
Ritorna 0però se il comando viene ucciso.
Stéphane Chazelas,

2
@bishop, sono d'accordo 0 in quei casi è meno che ideale. Ma nota che non esiste un accordo generale su cosa segnalare quando un comando viene ucciso da un segnale. Restituendo 128 + numero di segnale viene utilizzato da alcune shell. Puoi anche vedere cose come 256+signumo 384+signumo anche signum/256o signum/256+0.5se è stato generato un core (in realtà lo stato restituito waitpid()diviso per 256). Alcuni darebbero una rappresentazione testuale (come siginto sigquit+core). Probabilmente vale ancora la pena di discutere sul newsgroup gnu.coreutils.bugs che concordo.
Stéphane Chazelas,

3
(in realtà timenon fa parte di coreutils, è un pacchetto a sé stante con una propria mailing list di bug (bug-time@gnu.org))
Stéphane Chazelas

1
@bishop, i system()tradizionali dischi come quelli ancora gestiti da Brian Kernighan (gli kin awk) o quelli nawkdi Solaris, quelli awkdi FreeBSD che ne derivano. awk 'BEGIN{print system("kill -s ABRT $$")}'viene emesso 0.523438quando la dimensione del dump core non è limitata. Il comportamento di gawkè cambiato di recente, con un comportamento variabile con --traditionalo --posix(vedi anche il valore di ritorno di close(cmd)per ulteriori variazioni)
Stéphane Chazelas

2

Non mi piacciono tutte le altre risposte (anche se mi piace molto per la loro intelligenza): visualizzano il codice di uscita ma lo cambiano. (la parte visualizzata è vera, quindi dopo di essa il codice di ritorno è 0)

Ecco una versione modificata:

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

in realtà, la risposta di @bishop è l'unica altra che mantiene questo valore, ma si basa sulla versione di GNU di timecui non è sempre disponibile.
Olivier Dulac l'

2

Per essere un po 'più robusto, invia lo stato di uscita a un FD separato in modo che possa essere gestito indipendentemente da 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

Nota che dovrai fare qualcosa con FD3 o lo dirà Bad file descriptor.

Questo potrebbe essere ulteriormente configurato per fornire queste uscite agli ingressi di un altro programma facendo ascoltare l'altro programma su più di un FD; potresti usarlo come una specie di Kibana dei primi anni '90 :)


Dovrebbe mantenere il valore restituito originale e restituirlo alla fine. La visualizzazione (printf, echo) di una cosa semplice (non inizia con un '-', ecc.) Funziona sempre, e dopo quella visualizzazione "$?" ora ha il valore "0" mentre il display funzionava
Olivier Dulac l'

1

Mentre tutte le altre (molto interessanti) risposte rispondono alla domanda esatta che l'OP ha posto, in pratica le cose sono di solito più semplici. È sufficiente salvare lo stato di uscita in una variabile (immediatamente dopo il comando) e segnalarlo o registrarlo nel modo desiderato. Ad esempio, stampare su / dev / stderr o scrivere / aggiungere come messaggio di testo in un file di registro. Inoltre, è anche conservato per l'uso nel processo decisionale / controllo del flusso nel codice successivo.

Se è in una funzione:

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
}

Se è direttamente in uno 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

(Anatra e copertura perché questo non risponde alla domanda esatta .)

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.