Last failed command in bash
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.
Related videos on Youtube
che
My name is Eimantas. I am Cocoa developer from Lithuania. Sometimes I tweet. Mandatory #SOreadytohelp
Updated on September 18, 2022Comments
-
che over 1 year
The
$?
variable holds the exit status of last run command. Is there a variable that holds last run command itself?-
Gilles 'SO- stop being evil' over 12 yearsRelated question on Stack Overflow: BASH: echoing the last command run
-
-
enzotib over 12 yearsIn bash there is a
BASH_COMMAND
variable, but do not seems to be useful in any way, apart from use in traps. -
che over 12 yearsThanks 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 holdingsome-command
? -
rozcietrzewiacz over 12 yearsAs 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 over 7 yearsthis 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 arraysBASH_LINENO
,BASH_SOURCE
, andFUNCNAME
to do a kind of stack trace. -
karfau over 2 yearsIf you only want to trigger the
trap
when an error occured (the case forset -e
), you can use the signal (last argument)ERR
instead ofEXIT
and get rid if theif
.