Journal de sortie du code de commande, similaire à la commande time

10

en utilisant

time sleep 1

rendements:

$ time sleep 1

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

puis-je utiliser une commande pour imprimer le code de sortie sleepou la commande que je veux exécuter?

Quelque chose aime:

$ log-exit-code sleep 1

peut-être cela suffit-il?

sleep 1 && echo "$?"
Alexander Mills
la source
3
Non, sleep 1 && echo $?imprimerait le code de la cellule dormante seulement quand il est nul ...
Satō Katsura
oh lol tu as raison
Alexander Mills
1
sleep 1 && echo "$?" || echo "$?"
jesse_b
2
J'ai ajouté une réponse car toutes les autres jusqu'à présent ignorent la valeur de retour d'origine, ce qui est mauvais (après ces réponses, $? Est toujours 0 car la dernière chose qu'ils font est un disply, qui fonctionne toujours. Ils ont tous oublié de garder la valeur de retour d'origine et la retourner après l'avoir affichée. Par conséquent, ils la montrent à un observateur, mais la «cachent» pour le reste du script, qui ne peut pas utiliser maintenant «$?» pour voir si cette commande importante retourne 0 ou autre chose ...)
Olivier Dulac
1
De plus, toutes les solutions ci-dessous peuvent facilement implémenter cette fonctionnalité. Par exemple, Kusalananda serait simplement:EXIT_CODE=$(tellexit command)
jesse_b

Réponses:

4

Dans mes tests jusqu'à présent, cela a fonctionné:

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

Il lui dit simplement de faire écho au code de sortie s'il réussit ou s'il échoue.

Comme Sato l'a souligné ci-dessous, cela revient essentiellement à:

command; echo "$?"

Une chose qui pourrait rendre la commande and / ou intéressante est quelque chose comme:

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

Si vous avez besoin que votre script agisse sur le code de sortie comme c'est le cas d'Olivier, ce n'est pas un problème. Votre script pourrait ressembler à quelque chose comme:

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
la source
13
Euh, comment est-ce mieux que juste command; echo $??
Satō Katsura
4
Je suppose que non. command; echo $?est une bien meilleure réponse.
jesse_b
euhhh cela semble être la meilleure option, donc je l'ai choisi comme réponse, si quelqu'un n'est pas d'accord, lmk
Alexander Mills
@AlexanderMills Vous seul savez quelle est la réponse la plus appropriée à votre question :-)
Kusalananda
1
Je n'aime pas ceci: cela change le code de sortie après command ; echo $?car echo $?toujours réussit et donc après cela, la nouvelle valeur de $? est maintenant 0. Il existe un moyen de conserver le code retour d'origine même après l'avoir affiché, au cas où ce code retour original serait utile plus tard (par exemple: dans un script, la vérification du code retour est importante avant de décider comment continuer). Voir ma réponse pour l'une des nombreuses solutions (la plupart des réponses ici peuvent être modifiées de la même manière)
Olivier Dulac
11

cmd && echo "$?"ne fonctionnerait pas car il n'imprimerait par nécessité que des zéros (le echone s'exécuterait qu'en cas de réussite de la commande précédente).

Voici une fonction shell courte pour vous:

tellexit () {
    "$@"

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

Cela imprime le code de sortie de la commande donnée de la même manière que la timecommande.

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

$ tellexit false
exit code       1

En redirigeant le printfvers /dev/ttydans la fonction, nous pouvons toujours utiliser tellexitdes redirections sans obtenir de courrier indésirable dans nos flux de sortie ou d'erreur standard:

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

En enregistrant le code de sortie dans une variable, nous pouvons le renvoyer à l'appelant:

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

Une version plus sophistiquée de la même fonction imprime également le signal qui a tué la commande si le code de sortie est supérieur à 128 (ce qui signifie qu'il s'est terminé en raison d'un signal):

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

Essai:

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

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

(La localchose nécessite ash/ pdksh/ bash/ zsh, ou vous pouvez la changer, typesetce que quelques autres shells comprennent également.)

Kusalananda
la source
cool, comment utiliseriez-vous tellexit, montrez-le en action pls
Alexander Mills
1
@AlexanderMills Voir la mise à jour.
Kusalananda
2
Il doit conserver la valeur de retour d'origine et la renvoyer à la fin.
Olivier Dulac
@OlivierDulac Très bonne observation! Je vais modifier.
Kusalananda
7

Utilisez une fonction shell wrapper. Probablement avec un nom différent.

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

(Bien sûr, cela corrompra la sortie standard, alors utilisez le ttycomme indiqué par @Kusalananda ou ne l'utilisez pas en dehors des contextes interactifs.)

En virant vers un territoire non transférable, certains shells peuvent signaler l'état de toutes les commandes dans un pipeline, pas seulement la dernière, par exemple dans ZSH si vous souhaitez que les défaillances soient signalées à partir d'un pipeline entier:

% 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 sinon, il ne se déclenche pas lorsqu'il n'y a pas d'erreur (sur le principe «aucune nouvelle n'est bonne»).

branler
la source
1
Il doit conserver la valeur de retour d'origine et la renvoyer à la fin. L'affichage (printf, echo) d'une chose simple (ne commence pas par un '-', etc.) fonctionne toujours, et après cet affichage "$?" a maintenant la valeur "0" pendant que l'affichage fonctionnait
Olivier Dulac
7

GNU timea une option pour cela:

time -f %x sleep 1
0

Passe par le code de sortie 1 , sauf s'il est tué par un signal 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 vous voulez connaître / gérer la situation de destruction de signal, passez -vet grep le stderr pour la chaîne Command terminated by signal.


1 Merci à Olivier Dulac d'avoir noté que le code de sortie passe.
2 Merci également à Stéphane Chazelas pour avoir souligné que le code de sortie du signal kill ne passe pas.

évêque
la source
1
Intéressant. GNU uniquement mais intéressant, car celui-ci conservera (probablement) le code retour d'origine pour le reste du code à suivre (c'est-à-dire qu'il ne le remplace pas par "0" comme le font les autres réponses)
Olivier Dulac
2
Il revient 0cependant si la commande est supprimée.
Stéphane Chazelas
2
@bishop, je suis d'accord 0 dans ces cas est loin d'être idéal. Mais notez que, il n'y a pas d'accord général sur ce qu'il faut signaler lorsqu'un commandement est tué par un signal. Le retour du numéro de signal 128 + est utilisé par quelques obus. Vous voyez également des choses comme 256+signumou 384+signumou même signum/256ou signum/256+0.5si un noyau a été généré (en fait, le statut renvoyé par waitpid()divisé par 256). Certains donneraient une représentation textuelle (comme sigintou sigquit+core). Il vaut probablement la peine de discuter du groupe de discussion gnu.coreutils.bugs, je suis d'accord.
Stéphane Chazelas
3
(en timefait ne fait pas partie coreutils, c'est un package à part entière avec sa propre liste de diffusion de bugs ([email protected]))
Stéphane Chazelas
1
@bishop, le system()des awks traditionnels comme celui encore maintenu par Brian Kernighan (l' kin awk) ou le nawkde Solaris, le awkde FreeBSD qui en dérivent. awk 'BEGIN{print system("kill -s ABRT $$")}'sort 0.523438lorsque la taille de vidage de mémoire n'est pas limitée. Le comportement de gawka changé récemment, avec un comportement variable avec --traditionalou --posix(voir aussi la valeur de retour de close(cmd)pour plus de variation)
Stéphane Chazelas
2

Je n'aime pas toutes les autres réponses (bien que j'aime beaucoup pour leur intelligence): ils affichent le code de sortie mais ils LE CHANGENT également. (la partie d'affichage est vraie, donc après elle le code retour est 0)

Voici une version modifiée:

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
la source
en fait, @bishop answer est la seule autre qui conserve cette valeur, mais repose sur la version de GNU timequi n'est pas toujours disponible.
Olivier Dulac
2

Pour être un peu plus robuste, envoyez l'état de sortie à un FD séparé afin qu'il puisse être géré indépendamment 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

Notez que vous devrez faire quelque chose avec FD3 ou cela vous dira Bad file descriptor.

Cela pourrait être configuré davantage pour fournir ces sorties aux entrées d'un autre programme en faisant écouter l'autre programme sur plus d'un FD; vous pouvez l'utiliser comme une sorte de Kibana du début des années 90 :)

onwsk8r
la source
Il doit conserver la valeur de retour d'origine et la renvoyer à la fin. L'affichage (printf, echo) d'une chose simple (ne commence pas par un '-', etc.) fonctionne toujours, et après cet affichage "$?" a maintenant la valeur "0" pendant que l'affichage fonctionnait
Olivier Dulac
1

Alors que toutes les autres réponses (très intéressantes) répondent à la question exacte posée par le PO, dans la pratique, les choses sont généralement plus simples. Il vous suffit d'enregistrer l'état de sortie dans une variable (immédiatement après la commande) et de le signaler ou de l'enregistrer comme vous le souhaitez. Par exemple, imprimer dans / dev / stderr ou l'écrire / l'ajouter sous forme de message texte dans un fichier journal. Et, il est également conservé pour une utilisation dans la prise de décision / contrôle de flux dans le code suivant.

Si c'est dans une fonction:

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 c'est directement dans 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

(Ducking et couvrant parce que cela ne répond pas à la question exacte .)

Joe
la source