$ @除了第一个参数


36

我需要编写一个以这种方式运行的shell脚本:

./myscript arg1 arg2_1 arg2_2 arg2_3 ....... arg2_#

脚本中有一个for循环

for i in $@

但是,据我所知,$ @包含$ 1到$($#-1)。但是对于我的程序,$ 1与$ 2 $ 3 $ 4等有明显的不同。我想从$ 2循环到结尾...我如何实现这一点?谢谢:)

Answers:


47

首先,请注意,$@不带引号是没有意义的,因此不应使用。$@只能在引号("$@")和列表上下文中使用。

for i in "$@" 符合列表上下文的条件,但是在这里,为了遍历位置参数,规范,最可移植且更简单的形式是:

for i
do something with "$i"
done

现在,要循环从第二个元素开始的元素,规范和最可移植的方法是使用shift

first_arg=$1
shift # short for shift 1
for i
do something with "$i"
done

之后shift,以前的内容$1已从列表中删除(但我们已经将其保存在中$first_arg),$2现在的内容现在在中$1。位置参数已向左移动 1位置(用于shift 2移动2 ...)。因此,基本上,我们的循环从过去的第二个参数循环到最后一个。

使用bash(和zshksh93,仅此而已),另一种方法是:

for i in "${@:2}"
do something with "$i"
done

但请注意,它不是标准sh语法,因此不应在以开头的脚本中使用#! /bin/sh -

zsh或中yash,您还可以执行以下操作:

for i in "${@[3,-3]}"
do something with "$i"
done

从第3个参数循环到第3个最后一个参数。

在中zsh$@也称为$argv数组。因此,要从数组的开头或结尾弹出元素,您还可以执行以下操作:

argv[1,3]=() # remove the first 3 elements
argv[-3,-1]=()

shift也可以被写1=()zsh

在中bash,您只能分配$@带有set内置元素的元素,因此要从末尾弹出3个元素,就像这样:

set -- "${@:1:$#-3}"

并从第3个循环到最后一个第3个:

for i in "${@:3:$#-5}"
do something with "$i"
done

POSIXly,要弹出的最后3个元素"$@",您需要使用循环:

n=$(($# - 3))
for arg do
  [ "$n" -gt 0 ] && set -- "$@" "$arg"
  shift
  n=$((n - 1))
done

2
另一种(且很丑陋的)for ((i=2; i<=$#; i++)); do something with "${!i}"; done
打击

我对这个版本更加熟悉,因为我对c ++更加熟悉:)
user40780

10

我想您想要shift内置的。它重命名$2$1$3$2

像这样:

shift
for i in "$@"; do
    echo $i
done

您能否详细说明我如何在for循环中实现该目标?谢谢。
user40780

1
您不需要-在进入for循环之前先使用它,然后通常只需循环$ @即可。shift通话结束后,$ @应该是arg2_1 arg2_2 arg2_3...
John

但是,我还有一个问题:假设我想从$ 1循环到$($#-2)(即arg_1直到arg_2 _#-1,除了arg_2_#除外)...我该怎么办?
user40780

2

总是有穴居人的方法:

first=1
for i
do
        if [ "$first" ]
        then
                first=
                continue
        fi
        something with "$i"
done

这样就$@完好了(以防以后使用),并且只循环遍历每个参数,但不处理第一个参数。


1

在bash中,您还可以使用显式索引编写该循环:

for ((i=2; i<=$#; ++i)); do
  process "${!i}"
done

这将迭代从第二个参数到最后一个参数的所有参数。如果您想排除最后一个参数,只需执行以下操作

for ((i=1; i<=$#-1; ++i)); do
  process "${!i}"
done

如果只想接受其他所有参数,则将其写为

for ((i=1; i<=$#; i+=2)); do
  process "${!i}"
done

其背后的故事是内置函数的算术版本,结合了参数计数变量indirectfor$#${…}

一个不错的应用程序是您可以使用它来确定循环内给定选项是否将其后的参数作为值。如果是这样,则增加i(例如写入: $((++i)))以消耗下一个值,并在迭代过程中跳过它。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.