在bash shell脚本中传播所有参数


848

我正在编写一个非常简单的脚本,该脚本将调用另一个脚本,并且需要将参数从当前脚本传播到正在执行的脚本。

例如,我的脚本名称是foo.sh和调用bar.sh

foo.sh:

bar $1 $2 $3 $4

如何在不显式指定每个参数的情况下执行此操作?



Answers:


1402

如果您确实希望传递相同的参数,请使用"$@"而不是普通格式$@

观察:

$ cat foo.sh
#!/bin/bash
baz.sh $@

$ cat bar.sh
#!/bin/bash
baz.sh "$@"

$ cat baz.sh
#!/bin/bash
echo Received: $1
echo Received: $2
echo Received: $3
echo Received: $4

$ ./foo.sh first second
Received: first
Received: second
Received:
Received:

$ ./foo.sh "one quoted arg"
Received: one
Received: quoted
Received: arg
Received:

$ ./bar.sh first second
Received: first
Received: second
Received:
Received:

$ ./bar.sh "one quoted arg"
Received: one quoted arg
Received:
Received:
Received:

7
这样做是为了使引用/转义的字符串完全通过:观察:cat rsync_foo.sh#!/ bin / bash echo“ $ @” rsync“ $ @” ./rsync_foo.sh -n“ bar me” bar2禁止我bar2skipping directory bar me是否可以有可以看到真正的原始命令行并带有引号或转义字符串的shell脚本?
Mark Edington

3
那传递标志呢?例如,“ ./ bar.sh --with-stuff”
内森·朗

4
我建议任何想更好地理解分词的人,在这里阅读更多内容。
Rany Albeg Wein

4
我建议您也'arg with spaces'为三个示例说明在包含时发生的情况。结果令我感到惊讶。希望你能解释他们。
Michael Scheper

2
@MichaelScheper,无论如何,./foo.sh "arg with spaces"而且./foo.sh 'arg with spaces'都是100%相同的,所以我不认为将其添加到给出的示例中的建议会有什么帮助。
Charles Duffy

475

对于bash和其他类似Bourne的外壳:

java com.myserver.Program "$@"

13
@Amir:不,不是csh。为了每个人的理智:不要编写csh脚本。但是我认为也许$argv:q可以在某些csh变体中工作。
克里斯·约翰森

2
谢谢!根据需要,它传递脚本接收的相同参数集-而不是一个大参数。双引号是必需的。即使带引号的参数包含空格也可以使用。
安迪·托马斯

24
相关:如果您的shell脚本只是充当运行Java的包装,请考虑制作最后一行,exec java com.myserver.Program "$@"这会使bash在Java中执行,而不是等待它完成。因此,您减少了一个处理插槽。另外,如果父进程(运行您的脚本)正在通过pid监视它,并期望它是“ java”进程,那么如果您不执行exec,则可能会打破一些异常的事情;exec使java继承相同的pid。
greggo,2014年

7
@dragonxlwang:你可以使用一个数组变量,如果你的shell支持它们(如bash中zsh的,别人的,但不是纯Bourne-或POSIX的shell):保存到ARGSargs=("$@")和扩大每个元素作为一个单独的壳“字”(类似于"$@")与"${args[@]}"
克里斯·约翰森

1
使用时是否要记住任何“陷阱” "$@",例如如果您在参数中转义了空格,空字符或其他特殊字符,它将失败吗?
IQAndreas '16

97

使用"$@"(适用于所有POSIX兼容产品)。

[...] bash具有“ $ @”变量,该变量扩展为所有用空格分隔的命令行参数。

Bash为例


6
那么,“ $ @”是否不仅在$ @周围引起引用,而且实际上是一个不同的内置变量?
2015年

5
@ben它是一个变量,但需要用双引号引起来,以使其具有与(断点)不同的有用值$*。我相信这里有历史的进步;$*没有按设计工作,因此$@发明了它来代替它;但是引号规则就是它们的本质,仍然需要在其周围加上双引号(否则它将恢复为已损坏的$*语义)。
2015年

7
“那么,“ $ @”不仅在$ @周围引起引用,而且实际上是一个不同的内置变量吗?” -出于所有意图和目的,是的:stackoverflow.com/a/28099707/162094
Stuart Berg

1
如果调用包含echo "$@"as 的脚本,./script.sh a "b c" d那么您将得到a b c d而不是a "b c" d,这是非常不同的。
isarandi

7
@isarandi虽然的确输出不再包含引号了,但这确实是您所期望的...但是请放心,在脚本中,它echo会接收三个参数:("a" "b c" "d"然后,shell将它们作为字符串扩展的一部分连接在一起)。但是如果你用过的话,你一定for i in "$@"; do echo $i; done会得到的a⏎b c⏎d
FeRD

64

我知道这个问题已经得到很好的回答,但这是“ $ @” $ @“ $ *”和$ *之间的比较

测试脚本的内容:

# cat ./test.sh
#!/usr/bin/env bash
echo "================================="

echo "Quoted DOLLAR-AT"
for ARG in "$@"; do
    echo $ARG
done

echo "================================="

echo "NOT Quoted DOLLAR-AT"
for ARG in $@; do
    echo $ARG
done

echo "================================="

echo "Quoted DOLLAR-STAR"
for ARG in "$*"; do
    echo $ARG
done

echo "================================="

echo "NOT Quoted DOLLAR-STAR"
for ARG in $*; do
    echo $ARG
done

echo "================================="

现在,使用各种参数运行测试脚本:

# ./test.sh  "arg with space one" "arg2" arg3
=================================
Quoted DOLLAR-AT
arg with space one
arg2
arg3
=================================
NOT Quoted DOLLAR-AT
arg
with
space
one
arg2
arg3
=================================
Quoted DOLLAR-STAR
arg with space one arg2 arg3
=================================
NOT Quoted DOLLAR-STAR
arg
with
space
one
arg2
arg3
=================================

1
很好的演示和对此答案的贡献。
Merlin

33
#!/usr/bin/env bash
while [ "$1" != "" ]; do
  echo "Received: ${1}" && shift;
done;

只是认为这在尝试测试args如何进入脚本时可能会更有用


6
这肯定无助于回答他的问题,但这确实有用。赞!
达里奥·鲁索

2
传递空参数时,它将中断。您应该检查$#
Sebi

好的args测试人员,同意""''作为一个论据,如果没有args,也保持沉默。我试图解决此问题,但需要for循环并使用进行计数$#。我刚刚在末尾添加了此内容:echo "End of args or received quoted null"
Merlin

8

我的SUN Unix有很多限制,即使“ $ @”也无法按需解释。我的解决方法是$ {@}。例如,

#!/bin/ksh
find ./ -type f | xargs grep "${@}"

顺便说一句,我必须有这个特定的脚本,因为我的Unix也不支持grep -r


这是一个Bash问题;您正在使用ksh
Tripleee

6

如果在包含$@引号的字符串中包含其他字符,则当有多个参数时,行为非常奇怪,引号内仅包含第一个参数。

例:

#!/bin/bash
set -x
bash -c "true foo $@"

产量:

$ bash test.sh bar baz
+ bash -c 'true foo bar' baz

但是首先要分配一个不同的变量:

#!/bin/bash
set -x
args="$@"
bash -c "true foo $args"

产量:

$ bash test.sh bar baz
+ args='bar baz'
+ bash -c 'true foo bar baz'

3
我不会否认这令人不安,但是"$@"在bash 的语义内,这实际上是有意义的。它还有助于说明$@和之间的主要区别$*以及为什么它们都有用。在bash(1)手册页的“特殊参数”部分:“ *-当扩展出现在双引号内时,它将扩展为具有每个参数[…]值的单个单词,"$*"即等于"$1c$2c...",其中c[ $IFS]。实际上,在第一个示例中使用$*代替$@将获得与第二个版本相同的输出。
FeRD

2
现在,将其与进行比较"$@"。再次从手册页中进行:“ @—当扩展出现在双引号内时,每个参数扩展为一个单独的单词。也就是说,"$@"等同于"$1" "$2"…如果双引号扩展出现在一个单词内,则将连接第一个参数的扩展原始单词的开头部分,最后一个参数的扩展与原始单词的最后部分结合在一起。” ...的确,如果您的代码已经存在bash -c "true foo $@ bar baz",则可以像test.sh one twonet 一样运行它bash -c 'true foo one' 'two bar baz'
FeRD '18

1
感谢您的文档引用和相关信息$*,我似乎忘记了它的存在
。– ColinM

嘿。相反,$@我刚开始使用Shell脚本时只是受到关注,我仍然要提醒自己它在那里。看到"$*"在脚本中使用是很常见的……然后作者意识到这是将所有参数都粉碎了,所以他们会尝试所有复杂的废话和分词"$*",或者通过循环来[重新] arg列表一步一步shift地将它们拉下来...只需使用即可$@解决。(也帮助bash使用相同的助记符访问数组成员:${var[*]}对于所有成员而言,它们都是一个单词,一个单词${var[@]}列表。)
FeRD

真正的问题是您使用bash -c的方式完全没有道理。
三人

4

有时您想传递所有参数,但前面要加上一个标志(例如--flag

$ bar --flag "$1" --flag "$2" --flag "$3"

您可以通过以下方式执行此操作:

$ bar $(printf -- ' --flag "%s"' "$@")

注意:为避免额外的字段拆分,您必须用%sand 引号$@,并且为了避免使用单个字符串,您不能引用的子外壳printf


3

正常工作,除非您有空格或转义字符。在这种情况下,我找不到捕获参数并将其发送到脚本内部的ssh的方法。

这可能是有用的,但是非常丑陋

_command_opts=$( echo "$@" | awk -F\- 'BEGIN { OFS=" -" } { for (i=2;i<=NF;i++) { gsub(/^[a-z] /,"&@",$i) ; gsub(/ $/,"",$i );gsub (/$/,"@",$i) }; print $0 }' | tr '@' \' )

3

很多答案在这里建议$@$*有或无报价,但没有一个似乎解释这些真的,为什么你应该这样。因此,让我从以下答案中窃取这一出色的摘要:

在此处输入图片说明

请注意,引号会造成所有不同,没有引号,它们的行为相同。

出于我的目的,我需要按原样将参数从一个脚本传递到另一个脚本,为此,最佳选择是:

# file: parent.sh
# we have some params passed to parent.sh 
# which we will like to pass on to child.sh as-is

./child.sh $*

注意没有引号,$@并且在上述情况下也应适用。


2

bar "$@" 相当于 bar "$1" "$2" "$3" "$4"

请注意,引号很重要!

"$@",或,在转义和连接方面$@,每个行为都会略有不同,如本stackoverflow答案中所述"$*"$*

一个密切相关的用例是将所有给定的参数传递到这样的参数中

bash -c "bar \"$1\" \"$2\" \"$3\" \"$4\""

我使用@kvantour答案的变体来实现这一点:

bash -c "bar $(printf -- '"%s" ' "$@")"

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.