获取传递给 shell 脚本的最后一个参数
Posted
技术标签:
【中文标题】获取传递给 shell 脚本的最后一个参数【英文标题】:Getting the last argument passed to a shell script 【发布时间】:2010-12-23 15:52:12 【问题描述】:$1
是第一个参数。$@
是所有参数。
如何找到传递给 shell 的最后一个参数 脚本?
【问题讨论】:
我使用的是 bash,但解决方案越便携越好。 faqs.org/faqs/unix-faq/faq/part2/section-12.html 使用也可以使用$ !# 仅适用于bash,Kevin Little's answer 建议使用简单的$!#
。使用bash -c 'echo $!#' arg1 arg2 arg3
对其进行测试。对于bash、ksh 和zsh,Dennis Williamson's answer 建议$@: -1
。此外$*: -1
也可以使用。使用zsh -c 'echo $*: -1' arg1 arg2 arg3
对其进行测试。但这不适用于dash、csh 和tcsh。
$!#
与$@: -1
不同,它也适用于参数扩展。您可以使用bash -c 'echo $!#%.*' arg1.out arg2.out arg3.out
进行测试。
【参考方案1】:
这仅适用于 Bash:
echo "$@: -1"
【讨论】:
对于那些(像我一样)想知道为什么需要空间的人,man bash 有这样的说法: > 请注意,负偏移量必须与冒号隔开至少一个空格以避免被与 :- 扩展混淆。 我一直在使用它,它只在 Windows 中的 MSYS2 bash 中中断。奇怪。 注意: 这个答案适用于 all Bash 数组,不像$@:$#
只适用于 $@
。如果您要将$@
复制到带有arr=("$@")
的新数组中,$arr[@]:$#
将是未定义的。这是因为$@
的第 0 个元素未包含在 "$@"
扩展中。
@Mr.Llama:另一个避免$#
的地方是在遍历数组时,因为在 Bash 中,数组是稀疏的,而 $#
将显示数组中的元素数量,但不一定指向最后一个元素(或元素+1)。换句话说,不应该使用for ((i = 0; i++; i < $#)); do something "$array[$i]"; done
而是使用for element in "$array[@]"; do something "$element"; done
或迭代索引:for index in "$!array[@]"; do something "$index" "$array[$index]"; done
如果您需要对索引的值做一些事情。
也适用于 Bourne shell,至少对我来说是这样【参考方案2】:
这有点小技巧:
for last; do true; done
echo $last
这个也很便携(同样,应该与 bash、ksh 和 sh 一起使用)并且它不会改变参数,这可能很好。
它使用 for
隐式循环参数的事实,如果你不告诉它循环什么,以及 for 循环变量没有作用域的事实:它们保留它们设置的最后一个值。
【讨论】:
@MichałŠrajer,我认为您的意思是冒号而不是逗号;) @MichałŠrajertrue
is part of POSIX.
使用旧的 Solaris,使用旧的 bourne shell(不是 POSIX),我必须写 "for last in "$@"; do true; done"
@mcoolive @LaurenceGolsalves 除了更便携之外,for last in "$@"; do :; done
也让意图更加清晰。
@mcoolive:它甚至可以在 1979 年的 unix v7 bourne shell 中工作。如果它同时在 v7 sh 和 POSIX sh 上运行,你将无法获得更多的便携性:)【参考方案3】:
$ set quick brown fox jumps
$ echo $*: -1:1 # last argument
jumps
$ echo $*: -1 # or simply
jumps
$ echo $*: -2:1 # next to last
fox
空格是必要的,这样它就不会被解释为default value。
请注意,这仅适用于 bash。
【讨论】:
最佳答案,因为它还包括倒数第二个参数。谢谢! 史蒂文,我不知道你做了什么才能进入受罚区,但我很喜欢你在这里的工作。 是的。简直是最好的。除了命令和最后一个参数$@: 1:$#-1
@DynoFu 谢谢你,你回答了我的下一个问题。所以一个转变可能看起来像:echo $@: -1 $@: 1:$#-1
,其中最后一个成为第一个,其余的向下滑动【参考方案4】:
对于 bash 3.0 或更高版本,最简单的答案是
_last=$!# # *indirect reference* to the $# variable
# or
_last=$BASH_ARGV # official built-in (but takes more typing :)
就是这样。
$ cat lastarg
#!/bin/bash
# echo the last arg given:
_last=$!#
echo $_last
_last=$BASH_ARGV
echo $_last
for x; do
echo $x
done
输出是:
$ lastarg 1 2 3 4 "5 6 7"
5 6 7
5 6 7
1
2
3
4
5 6 7
【讨论】:
$BASH_ARGV
在 bash 函数中不起作用(除非我做错了什么)。
BASH_ARGV 有当 bash 被调用(或函数)时的参数,而不是当前的位置参数列表。
另请注意BASH_ARGV
将给您带来的是最后一个参数的值,而不是简单的“最后一个值”。例如!:如果您提供一个参数,然后调用 shift,$@:$#
将不会产生任何结果(因为您移出了唯一的参数!),但是,BASH_ARGV
仍然会给您(以前的)最后一个参数.
注意,$!#
用sh XXX.sh 1 2 3
执行脚本时什么也得不到【参考方案5】:
以下内容对您有用。 @ 用于参数数组。 : 表示在。 $# 是参数数组的长度。所以结果是最后一个元素:
$@:$#
示例:
function afunction
echo $@:$#
afunction -d -o local 50
#Outputs 50
请注意,这仅适用于 bash。
【讨论】:
虽然示例是针对函数的,但脚本也以相同的方式工作。我喜欢这个答案,因为它简洁明了。 而且它并不老套。它使用语言的显式特性,而不是副作用或特殊的 qwerk。这应该是公认的答案。【参考方案6】:使用结合长度的索引:
echo $@:$#@
请注意,这仅适用于 bash。
【讨论】:
回声 $(@&$;&^$&%@^!@%^#**#&@*#@*#@(&*#_**^@^&( ^@&*&@) @Atul 你能解释一下你有趣的命令吗?【参考方案7】:在将最后一个参数与所有前一个参数分开时发现了这一点。 虽然一些答案确实得到了最后一个论点,但如果您还需要所有其他参数,它们并没有太大帮助。这效果更好:
heads=$@:1:$#-1
tail=$@:$#
请注意,这仅适用于 bash。
【讨论】:
Steven Penny's answer 更好一点:将$@: -1
用于last,$@: -2:1
用于倒数第二(等等.. .)。示例:bash -c 'echo $@: -1' prog 0 1 2 3 4 5 6
打印 6
。要保持当前 AgileZebra 的方法,请使用$@:$#-1:1
获得倒数第二个。示例:bash -c 'echo $@:$#-1:1' prog 0 1 2 3 4 5 6
打印 5
。 (和$@:$#-2:1
获得倒数第三个等等......)
AgileZebra 的答案提供了一种获取 除了最后一个 参数的方法,因此我不会说 Steven 的答案取代了它。不过好像没有理由用$((...))
减1,可以直接用$@:1:$# - 1
。
感谢 dkasak。更新以反映您的简化。【参考方案8】:
这适用于所有 POSIX 兼容的 shell:
eval last=\$$#
来源:http://www.faqs.org/faqs/unix-faq/faq/part2/section-12.html
【讨论】:
参见this comment 附加到较旧的相同答案。 我在这里看到的最简单的便携式解决方案。这个没有安全问题,@DennisWilliamson,根据经验,引用似乎是正确的,除非有办法将$#
设置为任意字符串(我不这么认为)。 eval last=\"\$$#\"
也有效,显然是正确的。不明白为什么不需要引号。
对于bash、zsh、dash 和ksh eval last=\"\$$#\"
很好。但对于csh 和tcsh 使用eval set last=\"\$$#\"
。请参阅此示例:tcsh -c 'eval set last=\"\$$#\"; echo "$last"' arg1 arg2 arg3
。【参考方案9】:
这是我的解决方案:
非常便携(所有 POSIX sh、bash、ksh、zsh)应该可以工作 不移动原始参数(移动副本)。 不使用eval
不遍历整个列表
不使用外部工具
代码:
ntharg()
shift $1
printf '%s\n' "$1"
LAST_ARG=`ntharg $# "$@"`
【讨论】:
很好的答案 - 简短、便携、安全。谢谢! 这是个好主意,但我有几个建议:首先应该在"$1"
和"$#"
周围添加引用(请参阅这个很好的答案unix.stackexchange.com/a/171347)。其次,echo
很遗憾是不可移植的(特别是对于-n
),所以应该使用printf '%s\n' "$1"
。
感谢@joki,我使用了许多不同的 unix 系统,我也不相信echo -n
,但是我不知道在任何 posix 系统上echo "$1"
会失败。无论如何,printf
确实更可预测 - 已更新。
@MichałŠrajer 考虑“$1”为“-n”或“--”的情况,例如ntharg 1 -n
或ntharg 1 --
可能在各种系统上产生不同的结果。您现在拥有的代码是安全的!
或者:last() shift $(($# - 1));printf %s "$1";
【参考方案10】:
从最旧到新的解决方案:
最便携的解决方案,甚至更旧的sh
(适用于空格和全局字符)(无循环,更快):
eval printf "'%s\n'" "\"\$$#\""
自 bash 2.01 版起
$ set -- The quick brown fox jumps over the lazy dog
$ printf '%s\n' "$!# $@:(-1) $@: -1 $@:~0 $!#"
dog dog dog dog dog
对于 ksh、zsh 和 bash:
$ printf '%s\n' "$@: -1 $@:~0" # the space beetwen `:`
# and `-1` is a must.
dog dog
对于“倒数第二个”:
$ printf '%s\n' "$@:~1:1"
lazy
使用 printf 解决以破折号开头的参数的任何问题(如 -n
)。
对于所有 shell 和旧版 sh
(适用于空格和全局字符)是:
$ set -- The quick brown fox jumps over the lazy dog "the * last argument"
$ eval printf "'%s\n'" "\"\$$#\""
The last * argument
或者,如果你想设置一个last
var:
$ eval last=\$$#; printf '%s\n' "$last"
The last * argument
对于“倒数第二个”:
$ eval printf "'%s\n'" "\"\$$(($#-1))\""
dog
【讨论】:
【参考方案11】:如果您使用的是 Bash >= 3.0
echo $BASH_ARGV[0]
【讨论】:
对于最后一个参数的旁边,做echo $BASH_ARGV[1]
ARGV
代表“参数向量”。【参考方案12】:
对于bash
,this comment 建议很优雅:
echo "$@:$#"
要使shellcheck
静音,请使用:
echo $*:$#
作为奖励,两者都可以在zsh
中使用。
【讨论】:
【参考方案13】:shift `expr $# - 1`
echo "$1"
这会将参数的数量减去 1,并返回第一个(也是唯一的)剩余参数,这将是最后一个。
我只在 bash 中测试过,但它应该也可以在 sh 和 ksh 中工作。
【讨论】:
shift $(($# - 1))
- 不需要外部实用程序。适用于 Bash、ksh、zsh 和 dash。
@Dennis:太好了!我不知道$((...))
语法。
您希望使用printf '%s\n' "$1"
以避免来自echo
的意外行为(例如-n
)。【参考方案14】:
我发现@AgileZebra 的答案(加上@starfry 的评论)最有用,但它将heads
设置为标量。数组可能更有用:
heads=( "$@: 1: $# - 1" )
tail=$@:$#@
请注意,这仅适用于 bash。
编辑:根据@f-hauri 的评论删除了不必要的$(( ))
。
【讨论】:
如何将头像转换为数组? 我已经添加了括号,例如echo "$heads[-1]"
打印heads
中的最后一个元素。还是我错过了什么?
$(( ))
的使用没用! "$@: 1 : $# - 1 "
也一样!【参考方案15】:
使用eval
的解决方案:
last=$(eval "echo \$$#")
echo $last
【讨论】:
用于间接引用的 eval 是多余的,更不用说不好的做法了,并且存在巨大的安全问题(该值不在 echo 中或 $() 之外引用。Bash 具有用于间接引用的内置语法,例如任何变量:!
所以last="$!#"
会以更安全、紧凑、内置、理智的方式使用相同的方法(间接引用 $#)。并正确引用。
在another answer 中查看更简洁的执行方式。
@MestreLion 在=
的右上方不需要引号。
@TomHale:对于$!#
的特定情况为真,但不是一般情况:如果内容包含文字空格,则仍需要引号,例如last='two words'
。无论内容如何,只有 $()
是空白安全的。【参考方案16】:
如果您想以非破坏性的方式进行,一种方法是将所有参数传递给函数并返回最后一个:
#!/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
如果您实际上不关心保留其他参数,则在函数中不需要它,但我很难想到您永远不想保留的情况其他参数,除非它们已经被处理过,在这种情况下,我会使用 process/shift/process/shift/... 顺序处理它们的方法。
我在这里假设您想要保留它们,因为您没有遵循顺序方法。此方法还处理没有参数的情况,返回“”。您可以通过插入注释掉的 else
子句轻松调整该行为。
【讨论】:
【参考方案17】:对于 tcsh:
set X = `echo $* | awk -F " " 'print $NF'`
somecommand "$X"
我很确定这将是一个可移植的解决方案,除了分配。
【讨论】:
我知道你很久以前就发布了这个,但是这个解决方案很棒 - 很高兴有人发布了一个 tcsh !【参考方案18】:阅读上述答案后,我编写了一个 Q&D shell 脚本(应该在 sh 和 bash 上工作)在 PGM.cpp 上运行 g++ 以生成可执行映像 PGM。它假定命令行上的最后一个参数是文件名(.cpp 是可选的),所有其他参数都是选项。
#!/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
【讨论】:
【参考方案19】:以下将LAST
设置为最后一个参数而不改变当前环境:
LAST=$(
shift $(($#-1))
echo $1
)
echo $LAST
如果不再需要其他参数并且可以转移,则可以简化为:
shift $(($#-1))
echo $1
出于便携性原因如下:
shift $(($#-1));
可以替换为:
shift `expr $# - 1`
也将$()
替换为我们得到的反引号:
LAST=`
shift \`expr $# - 1\`
echo $1
`
echo $LAST
【讨论】:
【参考方案20】:echo $argv[$#argv]
现在我只需要添加一些文本,因为我的答案太短而无法发布。我需要添加更多文本进行编辑。
【讨论】:
这应该在哪个 shell 中工作?不是 bash。不是鱼(有$argv
但没有$#argv
— $argv[(count $argv)]
在鱼中工作)。【参考方案21】:
这是我的复制功能的一部分:
eval echo $(echo '$'"$#")
要在脚本中使用,请执行以下操作:
a=$(eval echo $(echo '$'"$#"))
解释(最先嵌套):
$(echo '$'"$#")
返回 $[nr]
其中[nr]
是参数的数量。例如。字符串$123
(未展开)。
echo $123
在计算时返回第 123 个参数的值。
eval
只是将 $123
扩展为参数的值,例如last_arg
。这被解释为字符串并返回。
从 2015 年年中开始与 Bash 合作。
【讨论】:
这里已经多次介绍了 eval 方法,但是这个解释了它是如何工作的。可以进一步改进,但仍然值得保留。【参考方案22】:#! /bin/sh
next=$1
while [ -n "$next" ] ; do
last=$next
shift
next=$1
done
echo $last
【讨论】:
如果参数是空字符串,这将失败,但在 99.9% 的情况下都有效。【参考方案23】:试试下面的脚本找到最后一个参数
# 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
输出:
# ./arguments.sh
No Arguments supplied
# ./arguments.sh testing for the last argument value
Last Argument is: value
谢谢。
【讨论】:
我怀疑这会因 ./arguments.sh "last value" 而失败 感谢您检查 Thomas,我已尝试像您提到的那样执行 as 脚本 # ./arguments.sh "last value" 最后一个参数是: value 现在工作正常。 # ./arguments.sh "last value check with double" 最后一个参数是:double 问题是最后一个参数是“最后一个值”,而不是值。该错误是由包含空格的参数引起的。【参考方案24】:有一种更简洁的方法可以做到这一点。 bash 脚本的参数可以放入一个数组中,这使得处理元素变得更加简单。下面的脚本将始终打印传递给脚本的最后一个参数。
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]
样本输出
$ ./lastarg.sh 1 2 buckle my shoe
shoe
【讨论】:
【参考方案25】:使用参数扩展(删除匹配的开头):
args="$@"
last=$args##*
最后也很容易搞定:
prelast=$args% *
【讨论】:
***.com/a/37601842/1446229 的可能(不良)重复 这仅在最后一个参数不包含空格时有效。 如果最后一个参数是一个用双引号括起来的句子,并且该句子应该是一个参数,那么您的 prelast 将失败。【参考方案26】:要返回最近使用的命令的最后一个参数,请使用特殊参数:
$_
在这种情况下,如果在调用另一个命令之前在脚本中使用它,它将起作用。
【讨论】:
这不是问题要问的【参考方案27】:$ echo "$*: -1"
这将打印最后一个参数
【讨论】:
这在其他一些答案中已经提到过,比如this one。【参考方案28】:使用 GNU bash
版本 >= 3.0:
num=$# # get number of arguments
echo "$!num" # print last argument
【讨论】:
【参考方案29】:只需使用!$
。
$ mkdir folder
$ cd !$ # will run: cd folder
【讨论】:
这不是问题要问的以上是关于获取传递给 shell 脚本的最后一个参数的主要内容,如果未能解决你的问题,请参考以下文章