10

using

time sleep 1

yields:

$ time sleep 1

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

is there a command I can use to print the exit code of sleep or whatever command I want to run?

Something likes:

$ log-exit-code sleep 1

perhaps this sufficient?

sleep 1 && echo "$?"
  • 3
    Nope, sleep 1 && echo $? would print the sleeper cell's code only when it's zero... – Satō Katsura Sep 7 '17 at 19:55
  • oh lol you're right – Alexander Mills Sep 7 '17 at 20:02
  • 1
    sleep 1 && echo "$?" || echo "$?" – Jesse_b Sep 7 '17 at 20:19
  • 2
    I added an answer as all the other ones so far discard the original return value, which imo is bad (after those answers, $? is always 0 as the last thing they do is a disply, which always works. They all forgot to keep the original return value, and return it after displaying it. Therefore they just show it to an observer, but 'hide' it for the rest of the script, which can't use now "$?" to see if that important command return 0 or something else...) – Olivier Dulac Sep 8 '17 at 11:23
  • 1
    Also all the solutions below can easily implement this functionality. Kusalananda's for example would just be: EXIT_CODE=$(tellexit command) – Jesse_b Sep 8 '17 at 11:38
4

In my testing so far this has worked:

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

Just tells it to echo the exit code if it succeeds or if it fails.

As Sato pointed out below this is essentially the same as:

command; echo "$?"

One thing that could make the and/or command worthwhile is something like:

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

If you need your script to act on the exit code as is Olivier's concern, it is a non issue. Your script could look something like:

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, how's that better than just command; echo $? ? – Satō Katsura Sep 7 '17 at 20:27
  • 4
    I guess it's not. command; echo $? is a much better answer. – Jesse_b Sep 7 '17 at 20:42
  • uhhh this seems like it's the best option, so I chose it as the answer, if anyone disagrees, lmk – Alexander Mills Sep 8 '17 at 5:16
  • @AlexanderMills Only you knows which is the most appropriate answer to your question :-) – Kusalananda Sep 8 '17 at 7:56
  • 1
    I dislike this : this changes the exit code after command ; echo $? because echo $? always succeeds and therefore after it, the new value of $? is now 0. There is a way to keep the original return code even after displaying it, in case this original return code is useful later (For example: in a script, checking the return code is important before deciding how to continue). See my answer for one of several solutions (most of the answers here can be changed in the same way) – Olivier Dulac Sep 8 '17 at 11:18
11

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.)

  • cool, how would you use tellexit, show it in action pls – Alexander Mills Sep 7 '17 at 20:04
  • 1
    @AlexanderMills See update. – Kusalananda Sep 7 '17 at 20:10
  • 2
    It should keep the original return value and return it in the end. – Olivier Dulac Sep 8 '17 at 11:24
  • @OlivierDulac Really good observation! I will amend. – Kusalananda Sep 8 '17 at 11:28
7

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).

  • 1
    It should keep the original return value and return it in the end. Displaying (printf, echo) a simple thing (doesn't start with a '-', etc) always works, and after that display "$?" has now the value "0" as the display worked – Olivier Dulac Sep 8 '17 at 11:26
7

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.

  • 1
    Interresting. GNU only but interresting, as that one will (probably) keep the original return code for the rest of the code to follow (ie, it doesn't replace it with "0" as the other answers do) – Olivier Dulac Sep 8 '17 at 11:27
  • 2
    It returns 0 though if the command is killed. – Stéphane Chazelas Sep 8 '17 at 11:42
  • 2
    @bishop, I agree 0 in those cases is less than ideal. But note that, there's no general agreement as to what to report when a command is killed by a signal. Returning 128+signal number is used by a few shells. You also see things like 256+signum or 384+signum or even signum/256 or signum/256+0.5 if a core was generated (actually the status returned by waitpid() divided by 256). Some would give a textual representation (likesigint or sigquit+core). Still probably worth discussing on the gnu.coreutils.bugs newsgroup I agree. – Stéphane Chazelas Sep 8 '17 at 12:01
  • 3
    (actually time is not part of coreutils, it's a package on its own with its own bug mailing list (bug-time@gnu.org)) – Stéphane Chazelas Sep 8 '17 at 12:17
  • 1
    @bishop, the system() of traditional awks like the one still maintained by Brian Kernighan (the k in awk) or the nawk of Solaris, the awk of FreeBSD that derive from that. awk 'BEGIN{print system("kill -s ABRT $$")}' outputs 0.523438 when the core dump size is not limited. The behaviour of gawk has changed recently, with varying behaviour with --traditional or --posix (see also the return value of close(cmd) for more variation) – Stéphane Chazelas Sep 8 '17 at 13:47
2

To be a bit more robust, send the exit status to a separate FD so it can be handled independently of 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

Note you'll need to do something with FD3 or it will say Bad file descriptor.

This could be configured further to provide these outputs to inputs of another program by having the other program listen on more than one FD; you could use this as a sort of early 90's Kibana :)

  • It should keep the original return value and return it in the end. Displaying (printf, echo) a simple thing (doesn't start with a '-', etc) always works, and after that display "$?" has now the value "0" as the display worked – Olivier Dulac Sep 8 '17 at 11:26
1

I dislike all the other answers (albeit I like a lot for their cleverness): they display the exit code but they also CHANGE IT. (the displaying part is true, hence after it the return code is 0)

Here is a modified version :

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
  • actually, @bishop answer is the only other one that does keep this value, but relies on GNU's version of time which is not always available. – Olivier Dulac Sep 8 '17 at 11:28
1

While all the other (very interesting) answers address the exact question the OP asked, in practice, things are usually simpler. You just save the exit status to a variable (immediately following the command) and report or log it any way you want to. E.g. print to /dev/stderr or write/append it as a text message into a log file. And, it's also preserved for use in decision making/flow control in subsequent code.

If it's in a function:

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
}

If it's directly in a 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 and covering because this does not answer the exact question.)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.