Getting the last argument passed to a shell script
Solution 1
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.
Solution 2
This is Bash-only:
echo "${@: -1}"
Solution 3
$ 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.
Solution 4
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
Solution 5
The following will work for you.
- @ is for array of arguments.
- : means at
- $# is the length of the array of arguments.
So the result is the last element:
${@:$#}
Example:
function afunction{
echo ${@:$#}
}
afunction -d -o local 50
#Outputs 50
Note that this is bash-only.
Related videos on Youtube
Thomas
Owner and developer of TwistedWave, an audio editor written in C++, objective-c and Scheme thanks to Guile.
Updated on March 25, 2022Comments
-
Thomas over 2 years
$1
is the first argument.
$@
is all of them.How can I find the last argument passed to a shell script?
-
Thomas over 14 yearsI was using bash, but the more portable solution the better.
-
Inshallah over 14 years
-
Prateek Joshi over 8 yearsuse can also use ${ !# }
-
oHo almost 7 yearsFor only bash, the Kevin Little's answer proposes the simple
${!#}
. 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. -
Arch Stanton almost 6 years
${!#}
, unlike${@: -1}
, also works with parameter expansion. You can test it withbash -c 'echo ${!#%.*}' arg1.out arg2.out arg3.out
. -
Tom Hale almost 4 years
-
-
Thomas over 14 yearsThis will fail if an argument is the empty string, but will work in 99.9% of the cases.
-
SourceSeeker over 13 years
shift $(($# - 1))
- no need for an external utility. Works in Bash, ksh, zsh and dash. -
Laurence Gonsalves over 13 years@Dennis: Nice! I didn't know about the
$((...))
syntax. -
MestreLion almost 13 yearseval for indirect reference is overkill, not to mention bad practice, and a huge security concern (the value is not quote in echo or outside $(). Bash has a builtin syntax for indirect references, for any var:
!
Solast="${!#}"
would use the same approach (indirect reference on $#) in a much safer, compact, builtin, sane way. And properly quoted. -
Zombo over 12 yearsAlso for next to last argument, do
echo ${BASH_ARGV[1]}
-
SourceSeeker almost 12 yearsSee this comment attached to an older identical answer.
-
Paweł Nadolski over 11 years@MichałŠrajer, I think you meant colon and not comma ;)
-
e40 almost 11 yearsBest answer, since it also includes the next to last arg. Thanks!
-
Thomas over 10 yearsI suspect this would fail with ./arguments.sh "last value"
-
Ranjithkumar T over 10 yearsThank you for checking Thomas, I have tried to perform the as script like you mentinoed # ./arguments.sh "last value" Last Argument is: value is working fine now. # ./arguments.sh "last value check with double" Last Argument is: double
-
Thomas over 10 yearsThe problem is that the last argument was 'last value', and not value. The error is caused by the argument containing a space.
-
Palec over 9 yearsSee a cleaner way to perform the same in another answer.
-
Palec over 9 yearsThe simplest portable solution I see here. This one has no security problem, @DennisWilliamson, the quoting empirically seems to be done right, unless there is a way to set
$#
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. -
Beni Cherniavsky-Paskin over 9 yearsWhich shell is this suppossed to work in? Not bash. Not fish (has
$argv
but not$#argv
—$argv[(count $argv)]
works in fish). -
Big McLargeHuge over 9 years
$BASH_ARGV
doesn't work inside a bash function (unless I'm doing something wrong). -
user3295674 over 9 yearsI know you posted this forever ago, but this solution is great - glad someone posted a tcsh one!
-
foo about 9 yearsFor those (like me) wondering why is the space needed, man bash has this to say about it: > Note that a negative offset must be separated from the colon by at least one space to avoid being confused with the :- expansion.
-
Palec almost 9 yearsThe eval approach has been presented here many times already, but this one has an explanation of how it works. Could be further improved, but still worth keeping.
-
Rufflewind over 8 years@MichałŠrajer
true
is part of POSIX. -
oHo over 8 yearsThe Steven Penny's answer is a bit nicer: use
${@: -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
prints6
. 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
prints5
. (and${@:$#-2:1}
to get the third last and so on...) -
mcoolive over 8 yearsWith an old Solaris, with the old bourne shell (not POSIX), I have to write "for last in "$@"; do true; done"
-
Ján Lalinský over 7 yearsGreat answer - short, portable, safe. Thanks!
-
dkasak over 7 yearsAgileZebra's answer supplies a way of getting all but the last arguments so I wouldn't say Steven's answer supersedes it. However, there seems to be no reason to use
$((...))
to subtract the 1, you can simply use${@:1:$# - 1}
. -
oHo almost 7 years
-
Bruno Bronosky almost 7 yearsSteven, I don't know what you did to land in the Penalty Box, but I am loving your work on here.
-
playjava almost 7 yearsWhile the example is for a function, scripts also work the same way. I like this answer because it is clear and concise.
-
joki over 6 yearsThis is a great idea, but I have a couple of suggestions: Firstly quoting should be added both around
"$1"
and"$#"
(see this great answer unix.stackexchange.com/a/171347). Secondly,echo
is sadly non-portable (particularly for-n
), soprintf '%s\n' "$1"
should be used instead. -
joki over 6 yearsYou'd want to use
printf '%s\n' "$1"
in order to avoid unexpected behaviour fromecho
(e.g. for-n
). -
musicin3d over 6 yearsAnd it's not hacky. It uses explicit features of the language, not side effects or special qwerks. This should be the accepted answer.
-
Michał Šrajer over 6 yearsthanks @joki I worked with many different unix systems and I wouldn't trust
echo -n
either, however I am not aware on any posix system whereecho "$1"
would fail. Anyhow,printf
is indeed more predictable - updated. -
joki over 6 years@MichałŠrajer consider the case where "$1" is "-n" or "--", so for example
ntharg 1 -n
orntharg 1 --
may yield different results on various systems. The code you have now is safe! -
Xerz over 6 yearsPossible (poor) duplicate of stackoverflow.com/a/37601842/1446229
-
Palec over 6 yearsThis only works if the last argument does not contain a space.
-
Steven Lu over 6 yearsI've been using this and it breaks in MSYS2 bash in windows only. Bizarre.
-
Adrian Günter about 6 years@mcoolive @LaurenceGolsalves beside being more portable,
for last in "$@"; do :; done
also makes the intent much clearer. -
Serge Stroobandt almost 6 years
ARGV
stands for "argument vector." -
done almost 6 yearsThe BASH_ARGV has the arguments when bash was called (or to a function) not the present list of positional arguments.
-
Steven Lu almost 6 yearsNote also what
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. -
e2rabi over 5 yearsThanks this help me !
-
Dyno Fu over 5 yearsyes. simply the best. all but command and last argument
${@: 1:$#-1}
-
Mr. Llama over 5 yearsNote: This answer works for all Bash arrays, unlike
${@:$#}
which only works on$@
. If you were to copy$@
to a new array witharr=("$@")
,${arr[@]:$#}
would be undefined. This is because$@
has a 0th element that isn't included in"$@"
expansions. -
SourceSeeker over 5 years@Mr.Llama: Another place to avoid
$#
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 dofor ((i = 0; i++; i < $#)); do something "${array[$i]}"; done
and instead dofor 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. -
Mike about 5 years@DynoFu thank you for that, you answered my next question. So a shift might look like:
echo ${@: -1} ${@: 1:$#-1}
, where last becomes first and the rest slide down -
Tom Hale about 5 years@MestreLion quotes are not needed on the RHS of
=
. -
MestreLion about 5 years@TomHale: true for the particular case of
${!#}
, but not in general: quotes are still needed if content contains literal whitespace, such aslast='two words'
. Only$()
is whitespace-safe regardless of content. -
Dominik R over 4 years@mcoolive: it even works in the unix v7 bourne shell from 1979. you can't get more portable if it runs on both v7 sh and POSIX sh :)
-
Stephane over 4 yearsYour prelast fails if the last argument is a sentence enclosed in double quotes, with said sentence supposed to be one argument.
-
Stephane over 4 yearsHow would you convert the heads into an array ?
-
EndlosSchleife over 4 yearsI already did by adding the parentheses, for example
echo "${heads[-1]}"
prints the last element inheads
. Or am I missing something? -
Atul over 4 yearsecho $(@&$;&^$&%@^!@%^#**#&@*#@*#@(&*#_**^@^&(^@&*&@)
-
retnikt about 4 yearsThis is not what the question is asking
-
retnikt about 4 yearsThis is not what the question is asking
-
oguz ismail about 4 years@mcoolive On Solaris sh
for last do true; done
(no semicolon afterlast
) works. That actually works with all sh implementations I have on my computer. -
AgileZebra about 4 yearsThanks dkasak. Updated to reflect your simplification.
-
Léa Gris over 3 yearsAlternatively:
last() { shift $(($# - 1));printf %s "$1";}
-
Michał Šrajer over 3 years@LéaGris I believe you wound need to use
expr
instead of $(()) to make it work on all unix systems. -
Léa Gris over 3 years@MichałŠrajer i Think if you are the guy tasked to write scripts for that 40 years old legacy SCO Unix, you are likely to know your way with the man pages and printed manual because browsing stackoverflow with Mozaic is going to be quite challenging on its own.
-
GregD about 3 yearsWorks for Bourne shell also, at least for me
-
roamer about 3 yearsAttention,
${!#}
get nothing when execute script withsh XXX.sh 1 2 3
-
TrueY about 3 yearsMind the space between ':' and '-'! :) I missed that for the 1st time...
-
Eric Aya almost 3 yearsThis has already been mentioned in some other answers, such as this one.
-
Isin Altinkaya almost 3 years@Atul can you explain your interesting command?
-
Jani Uusitalo over 2 yearsNote that when called without arguments, this yields the value of $0, which may not be what's intended.
-
F. Hauri - Give Up GitHub over 2 yearsuse of
$(( ))
is useless!"${@: 1 : $# - 1 }"
will work same!