The $?
variable holds the exit status of last run command. Is there a variable that holds last run command itself?
5 Answers
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)"
-
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 arraysBASH_LINENO
,BASH_SOURCE
, andFUNCNAME
to do a kind of stack trace.– phyattCommented Nov 7, 2016 at 20:32 -
1Works in interactive shell but not well in script. The "DEBUG trap" approach below is better– Bing RenCommented Oct 19, 2023 at 4:16
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.
-
If 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
.– karfauCommented Jan 20, 2022 at 5:51
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
).
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.
-
In bash there is a
BASH_COMMAND
variable, but do not seems to be useful in any way, apart from use in traps.– enzotibCommented Oct 3, 2011 at 8:26 -
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 holdingsome-command
?– EimantasCommented Oct 3, 2011 at 9:10 -
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". Commented Oct 3, 2011 at 11:36
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.