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

Fra*_*rth 769 bash command-line-arguments

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

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

foo.sh:

bar $1 $2 $3 $4
Run Code Online (Sandbox Code Playgroud)

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

Sda*_*ons 1276

如果您真的希望您的参数传递相同,请使用"$@"而不是plain $@.

注意:

$ 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:
Run Code Online (Sandbox Code Playgroud)

  • 这对于引用/转义字符串的纯粹传递有效:观察:cat rsync_foo.sh#!/ bin/bash echo"$ @"rsync"$ @"./ rssync_foo.sh -n"bar me"bar2 bar me bar2skipping目录吧我是否有可能有shell脚本,可以看到真实的原始命令行完成引号或转义字符串? (5认同)
  • 我建议任何想要更好地理解Word Splitting主题的人,[在这里阅读更多相关内容.](http://mywiki.wooledge.org/WordSplitting) (4认同)
  • 我建议你展示在三个例子中加入''带空格的arg'时会发生什么.我对结果感到惊讶; 希望你能解释一下. (3认同)
  • 那传递标志呢?例如,“ ./ bar.sh --with-stuff” (2认同)
  • @MichaelScheper,无论如何,.. / foo.sh“带有空格的arg”和..foo.sh“带有空格的arg”是100%相同的,所以我看不出将其添加到示例中的建议给任何帮助。 (2认同)

Chr*_*sen 468

对于bash和其他类似Bourne的shell:

java com.myserver.Program "$@"
Run Code Online (Sandbox Code Playgroud)

  • 相关:如果您的shell脚本只是作为运行java的包装器,请考虑使用最后一行`exec java com.myserver.Program"$ @"`这会导致bash执行java,而不是等待它完成.因此,您使用的是一个较少的进程槽.另外,如果父进程(运行你的脚本)通过pid观察它,并期望它是'java'进程,如果你不执行exec,一些不寻常的事情可能会破坏; exec导致java继承相同的pid. (24认同)
  • @Amir:不,不是*csh*.为了每个人的理智:[不要编写脚本csh](http://www.faqs.org/faqs/unix-faq/shell/csh-whynot/).但我想也许`$ argv:q`可以在一些csh变种中起作用. (12认同)
  • @dragonxlwang:你可以使用数组变量,如果你的shell支持它们(例如*bash*,*zsh*,其他,但不是普通的Bourne-或POSIX-shell):使用`args =保存到**args**( "$ @")`并将每个元素展开为一个单独的shell"word"(类似于"$ @"`)和``$ {args [@]}"`. (6认同)
  • 谢谢!根据需要,这会传递脚本收到的同一组参数 - 而不是一个大的参数.双引号是必要的.即使引用包含空格的参数也可以工作. (2认同)

mik*_*iku 93

使用"$@"(适用于所有POSIX兼容机).

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

Bash为例.

  • @ben它是一个单变量,但它需要它周围的双引号才能有一个与(损坏的)`$*`不同的有用值.我相信这里有历史进步; `$*`没有按设计工作,所以发明`$ @`来取代它; 但是引用规则就是它们,它周围的双引号仍然是必需的(或者它将恢复到破碎的`$*`语义). (5认同)
  • @isarandi虽然输出不再包含引号,但这是你所期望的......但请放心,在脚本中,`echo`会收到三个参数:`"a""bc""d"`(然后shell将它们连接在一起作为其字符串扩展的一部分).但如果你在"$ @"中使用`for i; 回声$ i; 完成了你已经得到了'a⏎bc⏎d`. (5认同)
  • 那么"$ @"不只是引用$ @,而实际上是一个不同的内置变量? (4认同)
  • “那么,“ $ @”不仅在$ @周围引起引用,而且实际上是一个不同的内置变量吗?” -对于所有意图和目的,是的:http://stackoverflow.com/a/28099707/162094 (4认同)
  • 如果您将包含 `echo "$@"` 的脚本调用为 `./script.sh a "b c" d`,那么您只会得到 `abcd` 而不是 `a "b c" d`,这是非常不同的。 (3认同)

JDS*_*JDS 48

我意识到这已经得到了很好的解答,但这里是"$ @"$ @"$*"和$*之间的比较

测试脚本的内容:

# 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 "================================="
Run Code Online (Sandbox Code Playgroud)

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

# ./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
=================================
Run Code Online (Sandbox Code Playgroud)


Shi*_*hah 34

这里有很多答案推荐$@$*带引号和不带引号,但是似乎没有人解释这些真正的作用以及为什么你应该这样做。所以让我从这个答案中窃取这个优秀的总结:

+--------+---------------------------+
| Syntax |      Effective result     |
+--------+---------------------------+
|   $*   |     $1 $2 $3 ... ${N}     |
+--------+---------------------------+
|   $@   |     $1 $2 $3 ... ${N}     |
+--------+---------------------------+
|  "$*"  |    "$1c$2c$3c...c${N}"    |
+--------+---------------------------+
|  "$@"  | "$1" "$2" "$3" ... "${N}" |
+--------+---------------------------+
Run Code Online (Sandbox Code Playgroud)

请注意,引号使一切变得不同,如果没有它们,它们都具有相同的行为。

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

# 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 $*
Run Code Online (Sandbox Code Playgroud)

注意没有引号,$@应该在上述情况下也能正常工作。

  • 你的例子是错误的。`$*` 传递给它们**不是**原样。也许这取决于你如何定义“原样”。示例:如果您调用 `./parent.sh 'a b' c`,则在父脚本中 `$1` 将计算为 `ab`,但在子脚本中 `$1` 将仅计算为 `a`。所以这不是我所期望的。我希望两个脚本“看到”相同的参数,并且这只适用于`./child.sh“$@”`。 (3认同)

Gre*_*ore 31

#!/usr/bin/env bash
while [ "$1" != "" ]; do
  echo "Received: ${1}" && shift;
done;
Run Code Online (Sandbox Code Playgroud)

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

  • 这绝对没有帮助回答他的问题,但这确实很有用.upvoted! (5认同)
  • 当传递空参数时它会中断。你应该检查`$#` (2认同)

Ham*_*123 8

我的SUN Unix有很多限制,甚至"$ @"也没有被解释为所希望的.我的解决方法是$ {@}.例如,

#!/bin/ksh
find ./ -type f | xargs grep "${@}"
Run Code Online (Sandbox Code Playgroud)

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

  • 这是一个 Bash 问题;你正在使用`ksh` (4认同)

Col*_*inM 6

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

例:

#!/bin/bash
set -x
bash -c "true foo $@"
Run Code Online (Sandbox Code Playgroud)

产量:

$ bash test.sh bar baz
+ bash -c 'true foo bar' baz
Run Code Online (Sandbox Code Playgroud)

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

#!/bin/bash
set -x
args="$@"
bash -c "true foo $args"
Run Code Online (Sandbox Code Playgroud)

产量:

$ bash test.sh bar baz
+ args='bar baz'
+ bash -c 'true foo bar baz'
Run Code Online (Sandbox Code Playgroud)

  • 我不会否认这令人不安,但是在bash中“ $ @”`的语义内,这实际上是有意义的。它还可以帮助说明$$和$ *之间的主要区别,以及它们为什么都有用。从`bash(1)`手册页的“特殊参数”部分:“`*`—当在双引号内进行扩展时,它将扩展为具有每个参数[…]值的单个单词,即`“ $ *” `等效于`“ $ 1c $ 2c ...”`,其中`c`是[`$ IFS`]。确实,在第一个示例中使用$$而不是$ @将获得与第二个版本相同的输出。 (3认同)
  • 现在,将其与`“ $ @”`进行比较。再次在手册页中:“`@`—当扩展出现在双引号中时,每个参数都扩展为一个单独的单词。也就是说,”“ $ @”`等效于`“ $ 1”``“ $ 2”`…如果在单词内出现双引号扩展名,则第一个参数的扩展名与原始单词的开头部分相连,而最后一个参数的扩展名与原始单词的末尾部分相连。” …的确,如果您的代码是`bash -c“ true foo $ @ bar baz”`,则将其作为`test.sh一二运行”将得到`bash -c'true foo one''两栏baz'`。 (2认同)

kva*_*our 6

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

$ bar --flag "$1" --flag "$2" --flag "$3"
Run Code Online (Sandbox Code Playgroud)

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

$ bar $(printf -- ' --flag "%s"' "$@")
Run Code Online (Sandbox Code Playgroud)

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


Dav*_*adi 5

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

请注意,引号很重要!

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

一个密切相关的用例是将所有给定的参数传递到一个参数中,如下所示:

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

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

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