$1
is the first argument.
$@
is all of them.
How can I find the last argument passed to a shell script?
This is Bash-only:
echo "${@: -1}"
${@:$#}
which only works on $@
. If you were to copy $@
to a new array with arr=("$@")
, ${arr[@]:$#}
would be undefined. This is because $@
has a 0th element that isn't included in "$@"
expansions.
Commented
Mar 18, 2019 at 17:26
$#
is when iterating over arrays since, in Bash, arrays are sparse and while $#
will show the number of elements in the array it's not necessarily pointing to the last element (or element+1). In other words, one shouldn't do for ((i = 0; i++; i < $#)); do something "${array[$i]}"; done
and instead do for element in "${array[@]}"; do something "$element"; done
or iterate over the indices: for index in "${!array[@]}"; do something "$index" "${array[$index]}"; done
if you need to do something with the values of the indices.
Commented
Mar 18, 2019 at 17:38
This is a bit of a hack:
for last; do true; done
echo $last
This one is also pretty portable (again, should work with bash, ksh and sh) and it doesn't shift the arguments, which could be nice.
It uses the fact that for
implicitly loops over the arguments if you don't tell it what to loop over, and the fact that for loop variables aren't scoped: they keep the last value they were set to.
for last in "$@"; do :; done
also makes the intent much clearer.
Commented
Apr 23, 2018 at 18:59
$ set quick brown fox jumps
$ echo ${*: -1:1} # last argument
jumps
$ echo ${*: -1} # or simply
jumps
$ echo ${*: -2:1} # next to last
fox
The space is necessary so that it doesn't get interpreted as a default value.
Note that this is bash-only.
${@: 1:$#-1}
echo ${@: -1} ${@: 1:$#-1}
, where last becomes first and the rest slide down
The simplest answer for bash 3.0 or greater is
_last=${!#} # *indirect reference* to the $# variable
# or
_last=$BASH_ARGV # official built-in (but takes more typing :)
That's it.
$ cat lastarg
#!/bin/bash
# echo the last arg given:
_last=${!#}
echo $_last
_last=$BASH_ARGV
echo $_last
for x; do
echo $x
done
Output is:
$ lastarg 1 2 3 4 "5 6 7"
5 6 7
5 6 7
1
2
3
4
5 6 7
$BASH_ARGV
doesn't work inside a bash function (unless I'm doing something wrong).
Commented
Dec 28, 2014 at 15:46
BASH_ARGV
will yield you is the value that the last arg that was given was, instead of simply "the last value". For example!: if you provide one single argument, then you call shift, ${@:$#}
will produce nothing (because you shifted out the one and only argument!), however, BASH_ARGV
will still give you that (formerly) last argument.
Commented
Sep 24, 2018 at 20:13
${!#}
get nothing when execute script with sh XXX.sh 1 2 3
/bin/bash
). You ran it through POSIX sh (/bin/sh
), which on many systems is not bash.
Commented
Mar 7, 2023 at 18:51
The following will work for you.
So the result is the last element:
${@:$#}
Example:
function afunction{
echo ${@:$#}
}
afunction -d -o local 50
#Outputs 50
Note that this is bash-only.
Use indexing combined with length of:
echo ${@:${#@}}
Note that this is bash-only.
Found this when looking to separate the last argument from all the previous one(s). Whilst some of the answers do get the last argument, they're not much help if you need all the other args as well. This works much better:
heads=${@:1:$#-1}
tail=${@:$#}
Note that this is bash-only.
${@: -1}
for last and ${@: -2:1}
for second last (and so on...). Example: bash -c 'echo ${@: -1}' prog 0 1 2 3 4 5 6
prints 6
. To stay with this current AgileZebra's approach, use ${@:$#-1:1}
to get the second last. Example: bash -c 'echo ${@:$#-1:1}' prog 0 1 2 3 4 5 6
prints 5
. (and ${@:$#-2:1}
to get the third last and so on...)
$((...))
to subtract the 1, you can simply use ${@:1:$# - 1}
.
This works in all POSIX-compatible shells:
eval last=\${$#}
Source: http://www.faqs.org/faqs/unix-faq/faq/part2/section-12.html
$#
to an arbitrary string (I don’t think so). eval last=\"\${$#}\"
also works and is obviously correct. Don’t see why the quotes are not needed.
Here is mine solution:
eval
Code:
ntharg() {
shift $1
printf '%s\n' "$1"
}
LAST_ARG=`ntharg $# "$@"`
"$1"
and "$#"
(see this great answer unix.stackexchange.com/a/171347). Secondly, echo
is sadly non-portable (particularly for -n
), so printf '%s\n' "$1"
should be used instead.
echo -n
either, however I am not aware on any posix system where echo "$1"
would fail. Anyhow, printf
is indeed more predictable - updated.
Commented
Jan 9, 2018 at 14:11
last() { shift $(($# - 1));printf %s "$1";}
From oldest to newer solutions:
The most portable solution, even older sh
(works with spaces and glob characters) (no loop, faster):
eval printf "'%s\n'" "\"\${$#}\""
Since version 2.01 of bash
$ set -- The quick brown fox jumps over the lazy dog
$ printf '%s\n' "${!#} ${@:(-1)} ${@: -1} ${@:~0} ${!#}"
dog dog dog dog dog
For ksh, zsh and bash:
$ printf '%s\n' "${@: -1} ${@:~0}" # the space beetwen `:`
# and `-1` is a must.
dog dog
And for "next to last":
$ printf '%s\n' "${@:~1:1}"
lazy
Using printf to workaround any issues with arguments that start with a dash (like -n
).
For all shells and for older sh
(works with spaces and glob characters) is:
$ set -- The quick brown fox jumps over the lazy dog "the * last argument"
$ eval printf "'%s\n'" "\"\${$#}\""
The last * argument
Or, if you want to set a last
var:
$ eval last=\${$#}; printf '%s\n' "$last"
The last * argument
And for "next to last":
$ eval printf "'%s\n'" "\"\${$(($#-1))}\""
dog
If you are using Bash >= 3.0
echo ${BASH_ARGV[0]}
For bash
, this comment suggested the very elegant:
echo "${@:$#}"
To silence shellcheck
, use:
echo ${*:$#}
As a bonus, both also work in zsh
.
shift `expr $# - 1`
echo "$1"
This shifts the arguments by the number of arguments minus 1, and returns the first (and only) remaining argument, which will be the last one.
I only tested in bash, but it should work in sh and ksh as well.
shift $(($# - 1))
- no need for an external utility. Works in Bash, ksh, zsh and dash.
Commented
Nov 9, 2010 at 2:32
$((...))
syntax.
Commented
Nov 11, 2010 at 16:48
printf '%s\n' "$1"
in order to avoid unexpected behaviour from echo
(e.g. for -n
).
I found @AgileZebra's answer (plus @starfry's comment) the most useful, but it sets heads
to a scalar. An array is probably more useful:
heads=( "${@: 1: $# - 1}" )
tail=${@:${#@}}
Note that this is bash-only.
Edit: Removed unnecessary $(( ))
according to @f-hauri's comment.
echo "${heads[-1]}"
prints the last element in heads
. Or am I missing something?
Commented
Dec 11, 2019 at 12:23
$(( ))
is useless! "${@: 1 : $# - 1 }"
will work same!
Commented
Dec 25, 2021 at 10:59
A solution using eval
:
last=$(eval "echo \$$#")
echo $last
!
So last="${!#}"
would use the same approach (indirect reference on $#) in a much safer, compact, builtin, sane way. And properly quoted.
Commented
Aug 7, 2011 at 16:28
${!#}
, but not in general: quotes are still needed if content contains literal whitespace, such as last='two words'
. Only $()
is whitespace-safe regardless of content.
Commented
Jun 18, 2019 at 21:23
If you want to do it in a non-destructive way, one way is to pass all the arguments to a function and return the last one:
#!/bin/bash
last() {
if [[ $# -ne 0 ]] ; then
shift $(expr $# - 1)
echo "$1"
#else
#do something when no arguments
fi
}
lastvar=$(last "$@")
echo $lastvar
echo "$@"
pax> ./qq.sh 1 2 3 a b
b
1 2 3 a b
If you don't actually care about keeping the other arguments, you don't need it in a function but I have a hard time thinking of a situation where you would never want to keep the other arguments unless they've already been processed, in which case I'd use the process/shift/process/shift/... method of sequentially processing them.
I'm assuming here that you want to keep them because you haven't followed the sequential method. This method also handles the case where there's no arguments, returning "". You could easily adjust that behavior by inserting the commented-out else
clause.
For tcsh:
set X = `echo $* | awk -F " " '{print $NF}'`
somecommand "$X"
I'm quite sure this would be a portable solution, except for the assignment.
To return the last argument of the most recently used command use the special parameter:
$_
In this instance it will work if it is used within the script before another command has been invoked.
After reading the answers above I wrote a Q&D shell script (should work on sh and bash) to run g++ on PGM.cpp to produce executable image PGM. It assumes that the last argument on the command line is the file name (.cpp is optional) and all other arguments are options.
#!/bin/sh
if [ $# -lt 1 ]
then
echo "Usage: `basename $0` [opt] pgm runs g++ to compile pgm[.cpp] into pgm"
exit 2
fi
OPT=
PGM=
# PGM is the last argument, all others are considered options
for F; do OPT="$OPT $PGM"; PGM=$F; done
DIR=`dirname $PGM`
PGM=`basename $PGM .cpp`
# put -o first so it can be overridden by -o specified in OPT
set -x
g++ -o $DIR/$PGM $OPT $DIR/$PGM.cpp
The following will set LAST
to last argument without changing current environment:
LAST=$({
shift $(($#-1))
echo $1
})
echo $LAST
If other arguments are no longer needed and can be shifted it can be simplified to:
shift $(($#-1))
echo $1
For portability reasons following:
shift $(($#-1));
can be replaced with:
shift `expr $# - 1`
Replacing also $()
with backquotes we get:
LAST=`{
shift \`expr $# - 1\`
echo $1
}`
echo $LAST
echo $argv[$#argv]
Now I just need to add some text because my answer was too short to post. I need to add more text to edit.
$argv
but not $#argv
— $argv[(count $argv)]
works in fish).
Commented
Dec 16, 2014 at 16:02
This is part of my copy function:
eval echo $(echo '$'"$#")
To use in scripts, do this:
a=$(eval echo $(echo '$'"$#"))
Explanation (most nested first):
$(echo '$'"$#")
returns $[nr]
where [nr]
is the number of parameters. E.g. the string $123
(unexpanded).echo $123
returns the value of 123rd parameter, when evaluated.eval
just expands $123
to the value of the parameter, e.g. last_arg
. This is interpreted as a string and returned.Works with Bash as of mid 2015.
#! /bin/sh
next=$1
while [ -n "${next}" ] ; do
last=$next
shift
next=$1
done
echo $last
Try the below script to find last argument
# cat arguments.sh
#!/bin/bash
if [ $# -eq 0 ]
then
echo "No Arguments supplied"
else
echo $* > .ags
sed -e 's/ /\n/g' .ags | tac | head -n1 > .ga
echo "Last Argument is: `cat .ga`"
fi
Output:
# ./arguments.sh
No Arguments supplied
# ./arguments.sh testing for the last argument value
Last Argument is: value
Thanks.
There is a much more concise way to do this. Arguments to a bash script can be brought into an array, which makes dealing with the elements much simpler. The script below will always print the last argument passed to a script.
argArray=( "$@" ) # Add all script arguments to argArray
arrayLength=${#argArray[@]} # Get the length of the array
lastArg=$((arrayLength - 1)) # Arrays are zero based, so last arg is -1
echo ${argArray[$lastArg]}
Sample output
$ ./lastarg.sh 1 2 buckle my shoe
shoe
Using parameter expansion (delete matched beginning):
args="$@"
last=${args##* }
It's also easy to get all before last:
prelast=${args% *}
$ echo "${*: -1}"
That will print the last argument
Just use !$
.
$ mkdir folder
$ cd !$ # will run: cd folder
${!#}
. Test it usingbash -c 'echo ${!#}' arg1 arg2 arg3
. For bash, ksh and zsh, the Dennis Williamson's answer proposes${@: -1}
. Moreover${*: -1}
can also be used. Test it usingzsh -c 'echo ${*: -1}' arg1 arg2 arg3
. But that does not work for dash, csh and tcsh.${!#}
, unlike${@: -1}
, also works with parameter expansion. You can test it withbash -c 'echo ${!#%.*}' arg1.out arg2.out arg3.out
.