获取传递给 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łŠrajer true is part of POSIX. 使用旧的 Solaris,使用旧的 bo​​urne 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 -nntharg 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 脚本的最后一个参数的主要内容,如果未能解决你的问题,请参考以下文章

脚本中$

Linux脚本$

Linux学习总结(六十)shell脚本4-函数及数组

shell 常见特殊变量列表

从 shell 脚本 (bash) 的参数列表中删除最后一个参数

shell脚本获取传递的参数