在bash中从$@中删除第一个元素

164

我正在编写一段bash脚本,需要循环遍历传递到脚本中的参数。然而,第一个参数不应该被循环遍历,而是在循环之前需要进行检查。

如果我不需要删除第一个元素,我只需执行以下操作:

for item in "$@" ; do
  #process item
done

我可以修改循环以检查它是否处于第一次迭代并更改行为,但那似乎太过hackish。肯定有一种简单的方法可以提取第一个参数,然后循环遍历其余部分,但我找不到。

4个回答

181

使用 shift

在循环之前,先阅读第一个参数 $1(如果你想要检查的是脚本名称,则为 $0),然后使用 shift,接着遍历剩余的 $@

echo "$@";  # will echo all args
shift;  # will remove first arg from the "$@"
echo "$@";  # will echo all args except first one

12
我知道有一个简单的答案。 :) - Herms
1
值得点赞,但在Dennis的回答被接受后再点赞 :) - mgarciaisaia
@mgarciaisaia似乎更符合OP的要求:删除第一项。 - Mark Fox
3
这种表述非常模糊,不如下面 @nos 的回答有帮助。 - Charney Kaye
1
最好有一个示例,以避免不得不访问可能会消失的外部链接。 - Gewthen
链接现在指向一个被 Firefox 标记为安全风险的网站。 - Rob Osborne

170
另一种变体使用数组切片:
for item in "${@:2}"
do
    process "$item"
done

如果出于某种原因您想保留参数而不对其进行更改,那么这可能是有用的,因为shift是破坏性的。


7
谢谢你的${@:2}。不错的东西! - cosmixtew
正是我想要的。现在在 "${!1}${@:2}" 中不需要临时变量来解除引用第一个参数。 - Charlie Gorichanaz
@Herms 这应该是被接受的答案,更易读且不具有破坏性(与 shift 相比)。 - user1075613
美丽,非常感谢。 - user5063151
1
从技术上讲,当前被接受的答案是正确的,但实际上这也是正确的(因为移位是破坏性的)。这是一个更好的答案。因此理想情况下应该是答案。 - Kuldeep Dhaka
对于那些想知道为什么 shift 是破坏性的人:它实际上从参数列表中最终删除了该元素。参考:https://unix.stackexchange.com/a/174568/254204 - pico_prob

62
firstitem=$1
shift;
for item in "$@" ; do
  #process item
done

2
请记住,$0 通常是脚本名称。 - Amber
4
+1 表示展示了一个简单的示例;请注意,“in "$@"”部分是隐含的,可以省略。 - mklement0
1
@mklement0 你是说你只需这样做 "for item; do" ? - David Doria
4
可以的。尝试使用以下命令:set -- 1 'two' 'three four'; for item; do echo "[$item]"; done - mklement0

6
q=${@:0:1};[ ${2} ] && set ${@:2} || set ""; echo $q

编辑

> q=${@:1}
# gives the first element of the special parameter array ${@}; but ${@} is unusual in that it contains (? file name or something ) and you must use an offset of 1;

> [ ${2} ] 
# checks that ${2} exists ; again ${@} offset by 1
    > && 
    # are elements left in        ${@}
      > set ${@:2}
      # sets parameter value to   ${@} offset by 1
    > ||
    #or are not elements left in  ${@}
      > set ""; 
      # sets parameter value to nothing

> echo $q
# contains the popped element

使用普通数组的pop示例

   LIST=( one two three )
    ELEMENT=( ${LIST[@]:0:1} );LIST=( "${LIST[@]:1}" ) 
    echo $ELEMENT

请同时解释代码以增加教育性。 - László Papp
q=${@:0:1}(顺便说一下,你的解释错误地引用为 q=${@:1})应该是 q=${@:1:1} 更清晰: $@ 从索引 1 开始,可能与显式 $1$2 等参数并行 - 元素 0 没有值(不像它的显式对应 $0,它反映了 shell / 脚本文件)。如果 $2 包含嵌入空格,则 [ ${2} ] 将 _中断_;如果使用 [[ ${2} ]],则不会出现此问题。话虽如此,条件和 || 分支是不必要的:如果只有一个参数,则 ${@:2} 将简单地扩展为空字符串(续)。 - mklement0
然而,您应该使用 set -- 来确保看起来像选项的参数不会被 set 解释,并且在 echo 语句中应该对 ${@:2} 引用和 $q 引用加上双引号。此时我们得到:q=${@:1:1}; set -- "${@:2}"; echo "$q"。但是,q=${@:1:1} 只是另一种(更繁琐的)方式来表示 $1,其余部分本质上重新实现了 shift 命令;使用这些特性,我们只需输入:q=$1; shift; echo "$q" - mklement0
@mklement0 我在想,在哪里放置一个 printf 来清理路径是个好地方?(我希望我能更清楚些) - Tegra Detra
很遗憾,我不明白。什么路径?以什么方式清理? - mklement0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接