如何将传递给我的Bash脚本的所有参数传递给我的函数?

957

假设我有一个函数 abc() ,它将处理与分析传递给我的脚本的参数相关的逻辑。

我该如何将Bash脚本接收到的所有参数传递给abc()? 参数数量是可变的,因此我不能像这样硬编码传递的参数:

abc $1 $2 $3 $4
更好的做法是,我的函数是否可以访问脚本参数的变量?

3
可能是在bash shell脚本中传播所有参数的重复问题。(实际上,这个问题比链接中的那个更早发布。但链接中的问题具有更详细的答案和更具信息性的标题,因此可能更适合作为参考问题) - prosoitos
7个回答

1605
< p > $@ 变量会将所有通过空格分隔的命令行参数展开。以下是一个例子。

abc "$@"

当使用$@时,你应该(几乎)总是将其放在双引号中,以避免解析包含空格或通配符的参数出现错误(参见下文)。这适用于多个参数。它也可以在所有符合POSIX标准的Shell中使用。

值得注意的是,$0(通常是脚本的名称或路径)不在$@中。

Bash参考手册特殊参数部分$@扩展为从一开始的位置参数。当扩展发生在双引号内时,每个参数都会扩展为一个单独的单词。也就是说"$@"相当于"$1" "$2" "$3"...

传递一些参数:

如果你想传递除了第一个参数之外的所有参数,你可以先使用shift来“消耗”第一个参数,然后将"$@"传递给另一个命令来传递剩余的参数。在Bash(和zsh和ksh,但不是纯POSIX Shell,如dash),你可以使用数组切片的变体来实现此目的而不会干扰参数列表:"${@:3}"将会获得从"$3"开始的参数。如果传递了那么多参数,"${@:3:4}"将获得以"$3"开头的最多四个参数(即"$3" "$4" "$5" "$6")。

你可能不想做的事情:

"$*"将所有参数粘在一起形成一个单独的字符串(由空格或$IFS的第一个字符分隔)。这会失去参数内部和参数之间空格的区别,因此通常是个坏主意。虽然对于打印参数可能还可以,例如echo "$*",只要您不关心保留空格的区别。

将参数分配给普通变量(如args="$@")会像"$*"一样将所有参数混合在一起。如果您想将参数存储在变量中,请使用带有args=("$@")的数组(括号使其成为数组),然后将它们引用为例如"${args[0]}"等等。请注意,在Bash和ksh中,数组索引从0开始,因此$1将在args[0]中,等等。另一方面,zsh从1开始数组索引,因此$1将在args[1]中。而更基本的shell如dash根本没有数组。

留下双引号,使用$@$*,将尝试将每个参数拆分为单独的单词(基于空格或$IFS中的任何内容),并尝试将类似文件名通配符的任何内容扩展为匹配的文件名列表。这可能会产生非常奇怪的效果,几乎应该避免使用。(除了在zsh中,默认情况下不进行此扩展。)

3
阅读更多关于为什么在这里重要的双引号的内容:https://dev59.com/ZW445IYBdhLWcg3wgqnT#4824637 - Cadoiz
1
${@:3}zsh 中似乎无法正常工作。 - BiBi
2
附加注释:$* 将是一个字符串,其中 IFS 的第一个字符将作为连接元素。 - kvantour
1
@kvantour 如果$*被双引号包含,就会发生这种情况。如果没有引号,它就会被分割成单词并进行通配符扩展,因此它立即被重新分割为元素和任何包含$IFS字符的元素也将被分割(然后任何通配符都会被扩展)。最终结果是:没有双引号,$*$@得到相同的结果。 - Gordon Davisson
1
@GordonDavisson 没错。我发现这个信息确实缺失了,但它并不属于你的精彩回答。像现在这样的评论更好。 - kvantour
显示剩余5条评论

170

我需要的是这个的变体,我相信这将对其他人有用:

function diffs() {
        diff "${@:3}" <(sort "$1") <(sort "$2")
}

"${@:3}" 的意思是从第三个元素开始的数组中的所有成员。因此,该函数通过将前两个参数传递给 sort 并将所有其他参数传递给 diff 来实现排序差异,因此您可以类似于调用 diff 来调用它:

diffs file1 file2 [other diff args, e.g. -y]

17
当你有带参数的脚本,但又需要将这些参数传递给其他被调用的脚本时,${@:3}也非常好用。例如,我的一个项目有一个脚本可以轻松运行程序,并带有一个用于指定主类的参数。然后,${@:2}可以用来将剩余的参数传递给该入口点。 - Kat
12
@Kat已经提到了这一点,但为了澄清(以防您仍然有疑问):command "$@" 相当于 command $1 "${@:2}" - Mahn
2
哦,我的天啊!我亲亲你! - Clay Bridges

71

使用$@变量,它会将所有由空格分隔的命令行参数进行展开。

abc "$@"

这将把引号中的参数分成单独的参数,但当运行脚本时,这很少是人们所期望的。 - alkanen
2
不好意思,我的错误。在你的示例中引用$@时它可以工作,但是在描述文本中不使用引号时它无法工作。 - alkanen

66

这是一个简单的脚本:

#!/bin/bash

args=("$@")

echo Number of arguments: $#
echo 1st argument: ${args[0]}
echo 2nd argument: ${args[1]}

$# 是脚本接收到的参数数目。我发现用数组来访问它们更容易: args=("$@") 这行代码将所有参数放入 args 数组中。要访问它们,使用 ${args[index]}


3
第一个参数将是${args[0]} :o - Goulash
3
将 $@ 传递到数组中有什么好处,相比于按索引调用参数(例如 $1)? - Kingand
1
不知怎么错过了,已修复,感谢指出。 - Giuseppe Cardone
请记住:${args[0]}等同于$1而不是$0。因此,如果您希望脚本始终以root身份运行,则应在开头放置if [[ $(id -u) -ne 0 ]]; then sudo "$0" "$@"; exit 0; fi - Bruno Bronosky
1
任何未来的读者应该注意,这个例子中的 shebang 是不正确的。sh 不支持数组,这是 bash 的特性。唯一能够工作的原因是如果您的操作系统将 /bin/sh 符号链接到 bash 或者您使用 bash script.sh 调用脚本。 - WhiteAbeLincoln
显示剩余3条评论

49
值得一提的是,您可以使用这种语法指定参数范围。
function example() {
    echo "line1 ${@:1:1}"; #First argument
    echo "line2 ${@:2:1}"; #Second argument
    echo "line3 ${@:3}"; #Third argument onwards
}

我没有看到它被提及过。


3
你可以只使用 $1$2 等来表示前两个。 - rubo77
@rubo77,我已经更正了我的回答中的措辞,包括“范围”,谢谢。 - robstarbuck
谢谢,这正是我需要使用变量索引$@的内容。 - rampion

32
abc "$@"

$@ 代表你的Bash脚本所接收到的所有参数。


16
如果您不引用$@,则会失去正确的单词拆分。 - Daenyth
@Daenyth是正确的,在这里阅读更多信息:https://dev59.com/ZW445IYBdhLWcg3wgqnT#4824637 - Cadoiz

31

abc "$@"通常是正确的答案。 但我想传递一个参数到su命令中,无论如何引用都无法阻止错误su: unrecognized option '--myoption'。对我实际起作用的是将所有参数作为单个字符串传递:

abc "$@"一般是正确的回答。但是,我试图通过su命令传递一个参数,不管怎样引用都无法阻止错误su: unrecognized option '--myoption'。对我而言有效的方法是将所有参数作为一个字符串传递:

abc "$*"

我的情况(我相信其他人也需要这个)发生在我的 .bashrc 文件中

# run all aws commands as Jenkins user
aws ()
{
    sudo su jenkins -c "aws $*"
}

2
嘿,楼主,看起来你在 abc 的例子中可能有一个打字错误。 - dannypaz
4
正是我所需的。我的命令被引号包裹,这是唯一对我起作用的方法。 - Toofy
2
可能是使用的方法,但在使用字符串参数时要注意风险。请查看此处的“$*”示例:https://dev59.com/ZW445IYBdhLWcg3wgqnT#46205560(这更多地涉及“$@”与“$*”以及它们的引用变体) - Cadoiz
我正在尝试理解它们之间的区别:在这两种情况下,bash 都会在将结果扩展为字符串后将变量($@$*)传递给 su。那么为什么一个字符串包含所有参数(从 $* 扩展),而另一个字符串不包含(从 $@ 扩展)? - Jens

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