【问题标题】:Process all arguments except the first one (in a bash script)处理除第一个参数之外的所有参数(在 bash 脚本中)
【发布时间】:2012-02-21 20:44:42
【问题描述】:

我有一个简单的脚本,其中第一个参数是为文件名保留的,所有其他可选参数都应该传递给脚本的其他部分。

使用 Google 我找到了this wiki,但它提供了一个文字示例:

echo "${@: -1}"

我不能做任何其他事情,比如:

echo "${@:2}"

echo "${@:2,1}"

我从终端收到“错误替换”。

有什么问题,除了第一个参数传递给 bash 脚本之外,我如何处理所有参数?

【问题讨论】:

  • 为了提醒其他困惑的人,提供了错误的 shebang 导致 "{@:2}" 不起作用,这就是正确答案与上面匹配的原因。
  • 您刚刚使用了默认 shell,它在 Ubuntu 和许多其他 Linux 上是 dash。在破折号中,“${@: -1}”被解释为:{parameter:-word} - 使用默认值,如果参数未定义或为空,则使用 word。所以在破折号中“${@: -1}”的结果与“$@”完全相同。要使用 bash,只需在脚本文件中使用以下第一行:#!/bin/bash

标签: bash shell


【解决方案1】:

使用这个:

echo "${@:2}"

以下语法:

echo "${*:2}"

也可以,但不推荐,因为正如@Gordon 已经解释的那样,使用*,它将所有参数作为带有空格的单个参数一起运行,而@ 保留它们之间的中断(即使某些参数本身包含空格)。它与echo 没有区别,但对许多其他命令很重要。

【讨论】:

  • 我刚刚意识到我的 shebang 很糟糕:#!/usr/bin/env sh 这就是我遇到问题的原因。在我删除那个 shebang 之后,您的示例工作正常,与上面提供的相同
  • 使用 "${@:2}" 代替 -- 使用 * 将所有参数作为带有空格的单个参数一起运行,而 @ 保留它们之间的中断(即使某些参数本身包含空格)。 echo 的区别并不明显,但它对许多其他事情都很重要。
  • @GordonDavisson 这里的重点是将参数一起运行。如果我们传递文件名,您将是正确的。 echo 足够宽容,可以为您连接它们;其他命令可能不太好。不要只使用其中一个:了解*@ 之间的区别,以及何时使用它们。您应该大致平等地使用它们。一个很好的例子来说明何时会出现问题:如果 $3 包含换行符 (\n),它将被替换为空格,前提是您有一个默认的 $IFS 变量。
  • @Zenexer:据我了解,问题是如何将除第一个参数之外的所有参数传递给“脚本的其他部分”,echo 仅用作示例——在这种情况下他们应该一起运行。根据我的经验,您希望它们一起运行的情况很少见(请参阅this question for one example),而"$@" 几乎总是您想要的。此外,您提到的换行问题仅在$@(或$*)不在双引号中时才会出现。
  • @GordonDavisson 嗯......你是对的,现在我想起来了;换行应该不是问题。我一定是在想别的东西。但是,我仍然不同意将它们一起运行。我需要在我的脚本中经常使用$*
【解决方案2】:

如果您想要一个同样适用于/bin/sh 的解决方案,请尝试

first_arg="$1"
shift
echo First argument: "$first_arg"
echo Remaining arguments: "$@"

shift [n] 移动位置参数 n 次。 shift$1 的值设置为$2 的值,将$2 的值设置为$3 的值,依此类推,将$# 的值减1。

【讨论】:

  • +1:您可能应该先将 $1 保存到变量中,然后再将其移走。
  • 令人惊讶的是foo=shift 并没有达到我的预期。
  • 我知道这是旧的,但试试foo=$(shift)
  • @raumaankidwai 这不起作用有两个原因:1)shift(在 shell 中)没有任何输出。它只是丢弃$1 并将所有内容向下移动。 2) $(...) 启动一个子shell,它有自己的本地参数。它移动子shell中的参数,这不会影响父级
  • 谢谢 :) 有什么办法可以做somecommand "$1" "${@:2}" 用这种方法做的事情(即转移“内联”)吗?
【解决方案3】:

使用 bash 4 或更高版本:

#!/bin/bash
echo "$0";         #"bash"
bash --version;    #"GNU bash, version 5.0.3(1)-release (x86_64-pc-linux-gnu)"

在函数中:

echo $@;              #"p1" "p2" "p3" "p4" "p5"
echo ${@: 0};  #"bash" "p1" "p2" "p3" "p4" "p5"
echo ${@: 1};         #"p1" "p2" "p3" "p4" "p5"
echo ${@: 2};              #"p2" "p3" "p4" "p5"
echo ${@: 2:1};            #"p2"
echo ${@: 2:2};            #"p2" "p3"
echo ${@: -2};                       #"p4" "p5"
echo ${@: -2:1};                     #"p4"

注意':'和'-'之间的空格,否则表示不同:

${var:-word} 如果 var 为 null 或未设置,
word 被替换为 var。 var 的值不会改变。

${var:+word} 如果设置了 var,
word 被替换为 var。 var 的值不会改变。

具体描述在:Unix / Linux - Shell Substitution

【讨论】:

    【解决方案4】:

    http://wiki.bash-hackers.org/scripting/posparams

    它解释了shift的使用(如果你想丢弃前N个参数)然后implementing Mass Usage

    【讨论】:

      【解决方案5】:

      在寻找其他东西时遇到了这个。 虽然这篇文章看起来相当陈旧,但下面使用set -- "${@:#}" 说明了 bash 中最简单的解决方案(至少是 bash 4),其中 # 是我们要向前保留的数组元素的起始编号:

          #!/bin/bash
      
          someVar="${1}"
          someOtherVar="${2}"
          set -- "${@:3}"
          input=${@}
      
          [[ "${input[*],,}" == *"someword"* ]] && someNewVar="trigger"
      
          echo -e "${someVar}\n${someOtherVar}\n${someNewVar}\n\n${@}"
      

      基本上,set -- "${@:3}" 只是像 perl 的 shift 一样弹出数组中的前两个元素,并保留包括第三个在内的所有剩余元素。我怀疑也有办法弹出最后一个元素。

      【讨论】:

        猜你喜欢
        • 2013-04-20
        • 1970-01-01
        • 2019-07-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-10-09
        • 1970-01-01
        • 2016-10-04
        相关资源
        最近更新 更多