【问题标题】:Getting the last argument passed to a shell script获取传递给 shell 脚本的最后一个参数
【发布时间】:2010-12-23 15:52:12
【问题描述】:

$1 是第一个参数。
$@ 是所有参数。

如何找到传递给 shell 的最后一个参数 脚本?

【问题讨论】:

  • 我使用的是 bash,但解决方案越便携越好。
  • 使用也可以使用${ !# }
  • 仅适用于bashKevin Little's answer 建议使用简单的${!#}。使用bash -c 'echo ${!#}' arg1 arg2 arg3 对其进行测试。对于bashkshzshDennis Williamson's answer 建议${@: -1}。此外${*: -1}也可以使用。使用zsh -c 'echo ${*: -1}' arg1 arg2 arg3 对其进行测试。但这不适用于dashcshtcsh
  • ${!#}${@: -1} 不同,它也适用于参数扩展。您可以使用bash -c 'echo ${!#%.*}' arg1.out arg2.out arg3.out 进行测试。

标签: shell arguments


【解决方案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=\"\${$#}\" 也有效,显然是正确的。不明白为什么不需要引号。
  • 对于bashzshdashksh eval last=\"\${$#}\" 很好。但对于cshtcsh 使用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】:

    对于bashthis 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 '$'"$#"))
            

            解释(最先嵌套):

            1. $(echo '$'"$#") 返回 $[nr] 其中[nr] 是参数的数量。例如。字符串$123(未展开)。
            2. echo $123 在计算时返回第 123 个参数的值。
            3. 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% *}
              

              【讨论】:

              • stackoverflow.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
                

                【讨论】:

                • 这不是问题要问的
                猜你喜欢
                • 2013-11-09
                • 1970-01-01
                • 1970-01-01
                • 2011-05-30
                • 2022-11-04
                • 1970-01-01
                • 1970-01-01
                • 2021-10-11
                • 1970-01-01
                相关资源
                最近更新 更多