Log exit code of command, similar to time command
cmd && echo "$?"
wouldn't work since it would by necessity only print zeroes (the echo
would only execute on successful completion of the preceding command).
Here's a short shell function for you:
tellexit () {
"$@"
local err="$?"
printf 'exit code\t%d\n' "$err" >/dev/tty
return "$err"
}
This prints the exit code of the given command in a similar manner as the time
command does.
$ tellexit echo "hello world"
hello world
exit code 0
$ tellexit false
exit code 1
By redirecting the printf
to /dev/tty
in the function, we may still use tellexit
with redirections without getting junk in our standard output or error streams:
$ tellexit bash -c 'echo hello; echo world >&2' >out 2>err
exit code 0
$ cat out
hello
$ cat err
world
By saving the exit code in a variable we are able to return it to the caller:
$ tellexit false || echo 'failed'
exit code 1
failed
A fancier version of the same function also prints the signal that killed the command if the exit code is greater than 128 (which means it terminated due to a 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"
}
Testing:
$ tellexit sh -c 'kill $$'
exit code 143 (TERM)
$ tellexit sh -c 'kill -9 $$'
Killed
exit code 137 (KILL)
(The local
thing requires ash
/pdksh
/bash
/zsh
, or you can change it to typeset
which a few other shells also understand.)
Use a shell wrapper function thingy. Probably with a different name.
$ exito() { "$@"; echo $?; }
$ exito true
0
$ exito false
1
$ exito echo "test test"
test test
0
$
(This of course will corrupt standard output, so either use the tty
as shown by @Kusalananda or do not use it outside of interactive contexts.)
Veering off into unportable territory, some shells can report on the status of all the commands in a pipeline, not just the last one, e.g. in ZSH if you want failures reported from an entire 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
otherwise does not fire when there is no error (on the "no news is good news" principal).
GNU time
has an option for this:
time -f %x sleep 1
0
Passes through the exit code1, unless killed by a signal2:
$ /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
If you want to know/handle the signal kill situation, pass -v
and grep the stderr for the string Command terminated by signal
.
1Thanks to Olivier Dulac for noting the exit code passes through.
2Also, thank you Stéphane Chazelas for pointing out kill signal exit code does not pass through.