Last failed command in bash

8,797

Solution 1

Use fc to get the previous command line. It is normally used to edit the previous command line in your favourite editor, but it has a "list" mode, too:

last_command="$(fc -nl -1)"

Solution 2

If the last command was executed without arguments, it'll be saved in the $_ variable. This normally contains the last argument of previous command - so if there were no arguments, the value of $_ is the last command itself.

Another option is to learn the details of last background command. As l0b0 wrote, $! holds its PID - so you can parse the output of ps $! (possibly with additional formating options to ps).

Solution 3

The DEBUG trap lets you execute a command right before any simple command execution. A string version of the command to execute (with words separated by spaces) is available in the BASH_COMMAND variable.

trap 'previous_command=$this_command; this_command=$BASH_COMMAND' DEBUG
…
echo "last command is $previous_command"

Note that previous_command will change every time you run a command, so save it to a variable in order to use it. If you want to know the previous command's return status as well, save both in a single command.

cmd=$previous_command ret=$?
if [ $ret -ne 0 ]; then echo "$cmd failed with error code $ret"; fi

If you only want to abort on a failed commands, use set -e to make your script exit on the first failed command. You can display the last command from the EXIT trap.

set -e
trap 'echo "exit $? due to $previous_command"' EXIT

An alternate approach that might work for some uses is to use set -x to print a trace of the script's execution and examine the last few lines of the trace.

Solution 4

No, but you can get it during execution to store for other commands:

  • $0: Path of the current shell script.
  • $FUNCNAME: "Name of the current function."
  • "$@": All the parameters of the current command, quoted separately.
  • $!: "PID (process ID) of last job run in background."
  • $$: "Process ID (PID) of the script itself."

The full command of the current script should therefore be "$0" "$@". If it's a function it should be "$FUNCNAME" "$@". You might want to store that in an array for future processing. For example, store this in test.sh:

#!/usr/bin/env bash
foo()
{
    declare -a command=("$0")
    for param in "$@"
    do
        command+=("$(printf %q "$param")")
    done
    echo "${command[@]}"
}
foo "$@"

When running ./test.sh "first argument" "second argument", it should return:

./test.sh first\ argument second\ argument

Which are equivalent calls.

Solution 5

I find it essential to find the last failed command when having set -e and set -o pipefail options, as otherwise bash simply aborts with no feedback on why, so this is what I found working well:

#!/usr/bin/env bash
set -eu
set -o pipefail

cur_command=
first_err_command=
first_err_lineno=
# This trap is executed in exactly the same conditions in which the `set -e` results in an exit.
trap 'cur_command=$BASH_COMMAND;
      if [[ -z "$first_err_command" ]]; then
          first_err_command=$cur_command;
          first_err_lineno=$LINENO;
      fi' ERR
trap 'if [[ ! -z "$first_err_command" ]]; then
          echo "ERROR: Aborting at line: $first_err_lineno on command: $first_err_command";
      fi' EXIT

echo "The following command causes bash to abort, but it should also result in a nice message"
false
echo "This message is not expected"

If you run the above, you will end up seeing the below below sort of output:

The following command causes bash to abort, but it should also result in a nice message
ERROR: Aborting at line: 22 on command: false

The line number may not always be accurate, but it should give you something close enough to be useful.

Share:
8,797

Related videos on Youtube

che
Author by

che

My name is Eimantas. I am Cocoa developer from Lithuania. Sometimes I tweet. Mandatory #SOreadytohelp

Updated on September 18, 2022

Comments

  • che
    che over 1 year

    The $? variable holds the exit status of last run command. Is there a variable that holds last run command itself?

  • enzotib
    enzotib over 12 years
    In bash there is a BASH_COMMAND variable, but do not seems to be useful in any way, apart from use in traps.
  • che
    che over 12 years
    Thanks for your input. What about if I run some-command in a shell script, and it fails. I'll have non-zero status in $?, will "no" still hold for the existence of variable holding some-command?
  • rozcietrzewiacz
    rozcietrzewiacz over 12 years
    As far as I know, the sole fact that a command failed does not change the set of information your shell stores about it. So I'd say Yes, "no".
  • phyatt
    phyatt over 7 years
    this sadly doesn't work well quite how I expected if there are large case statements in use or functions in use... :( I ended up using caller and the bash arrays BASH_LINENO, BASH_SOURCE, and FUNCNAME to do a kind of stack trace.
  • karfau
    karfau over 2 years
    If you only want to trigger the trap when an error occured (the case for set -e), you can use the signal (last argument) ERR instead of EXIT and get rid if the if.