我正在编写一个非常简单的脚本,调用另一个脚本,需要将当前脚本的参数传递给我要执行的脚本。
例如,我的脚本名称是foo.sh
,并调用了bar.sh
。
foo.sh:
bar $1 $2 $3 $4
如何在不显式指定每个参数的情况下完成这个操作?
我正在编写一个非常简单的脚本,调用另一个脚本,需要将当前脚本的参数传递给我要执行的脚本。
例如,我的脚本名称是foo.sh
,并调用了bar.sh
。
foo.sh:
bar $1 $2 $3 $4
如何在不显式指定每个参数的情况下完成这个操作?
如果您希望参数被传递相同,请使用"$@"
而不是普通的$@
。
观察:
$ cat no_quotes.sh
#!/bin/bash
echo_args.sh $@
$ cat quotes.sh
#!/bin/bash
echo_args.sh "$@"
$ cat echo_args.sh
#!/bin/bash
echo Received: $1
echo Received: $2
echo Received: $3
echo Received: $4
$ ./no_quotes.sh first second
Received: first
Received: second
Received:
Received:
$ ./no_quotes.sh "one quoted arg"
Received: one
Received: quoted
Received: arg
Received:
$ ./quotes.sh first second
Received: first
Received: second
Received:
Received:
$ ./quotes.sh "one quoted arg"
Received: one quoted arg
Received:
Received:
Received:
观察:
cat rsync_foo.sh
#!/bin/bash
echo "$@"
rsync "$@"
./rsync_foo.sh -n "bar me" bar2
bar me bar2skipping directory bar me
是否可能有一个Shell脚本可以查看真正的原始命令行并包括引号或转义字符串?
- Mark Edington'arg with spaces'
的情况。我对结果感到惊讶,希望您能解释一下。 - Michael Scheper对于bash和其他 Bourne-like shells:
bar "$@"
$argv:q
或许在某些 csh 变体中可以工作。 - Chris Johnsenexec java com.myserver.Program "$@"
。这会导致bash进入java,而不是等待它完成。因此,您将使用少一个进程插槽。此外,如果父进程(运行您的脚本)通过pid监视它,并期望它是“java”进程,如果您不执行exec,则可能会出现一些异常情况;exec导致java继承相同的pid。 - greggoargs=("$@")
将参数存储到args中,然后将每个元素作为单独的shell“word”扩展(类似于"$@"
)使用 "${args[@]}"
。 - Chris Johnsen"$@"
时,有没有需要注意的“陷阱”,例如如果参数中有转义空格、空字符或其他特殊字符,它是否会失败? - IQAndreasecho "$@"
的脚本,例如 ./script.sh a "b c" d
,那么你得到的结果是 a b c d
而不是 a "b c" d
,这两者之间有很大的区别。 - isarandiecho
命令接收到三个参数: "a" "b c" "d"
(然后 shell 将它们连接起来作为字符串扩展的一部分)。但如果你使用了 for i in "$@"; do echo $i; done
,你会得到 a⏎b c⏎d
的输出。 - FeRD我知道这个问题早已得到了很好的回答,但这里是"$@"、$@、"$*"和$*之间的比较。
测试脚本的内容:
# 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
=================================
这里有很多答案推荐使用带引号或不带引号的$@
或$*
,但是似乎没有人解释这些命令的真正作用和为什么应该这样使用。所以让我从这个答案中窃取这个精彩总结:
+--------+---------------------------+
| Syntax | Effective result |
+--------+---------------------------+
| $* | $1 $2 $3 ... ${N} |
+--------+---------------------------+
| $@ | $1 $2 $3 ... ${N} |
+--------+---------------------------+
| "$*" | "$1c$2c$3c...c${N}" |
+--------+---------------------------+
| "$@" | "$1" "$2" "$3" ... "${N}" |
+--------+---------------------------+
注意引号会使两者的行为有所不同,如果没有引号,则两者的行为相同。
根据我的需求,我需要将参数原封不动地从一个脚本传到另一个脚本,而最好的选择是:
# 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 $*
请注意上述情况中没有引号,$@
也应该能够正常工作。
env --debug
命令自行查看。例如,将 env --debug echo "$*"
放入一个函数中,并尝试使用不同的参数执行它。 - user2846495$*
不应该按照原样传递。也许这取决于你如何定义“按原样传递”。例如:如果你调用 ./parent.sh 'a b' c
,那么在父脚本中 $1
将会被解析为 a b
,但在子脚本中 $1
只会被解析为 a
。所以这不是我期望的情况。我期望两个脚本都“看到”相同的参数,而这只能通过使用 ./child.sh "$@"
来实现。 - bibermann#!/usr/bin/env bash
while [ "$1" != "" ]; do
echo "Received: ${1}" && shift;
done;
我觉得当你尝试测试参数如何进入你的脚本时,这可能会更有用。
$#
。 - Sebi""
和 ''
作为参数,如果没有参数,则保持沉默。我试图修复这个问题,但需要使用 $#
的 for 循环和计数器。我只是在最后添加了这个:echo "End of args or received quoted null"
。 - Merlinenv --debug <command> <args>
以获得类似的输出。例如:env --debug echo "$@"
。 - user2846495$@
与其他字符一起放在引号字符串中,则在有多个参数时,行为非常奇怪,只有第一个参数包含在引号内。#!/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'
"$@"
语义上,这实际上是有道理的。它还有助于说明 $@
和 $*
之间的关键区别以及它们各自的用处。从 bash(1)
手册的“特殊参数”部分可以得知:*
—— 当扩展出现在双引号内时,它将扩展为单个单词,其值为每个参数。也就是说,"$*"
等同于 "$1c$2c..."
,其中 c
是 [$IFS
]。事实上,在第一个例子中使用 $*
而不是 $@
会产生与第二个版本相同的输出。 - FeRD"$@"
进行比较。再次引用手册页面:“@” - 当扩展出现在双引号内时,每个参数都会扩展为单独的单词。也就是说,“$@”等同于“$1”“$ 2”… 如果双引号扩展出现在单词内,则第一个参数的扩展将与原始单词的开头部分连接,最后一个参数的扩展将与原始单词的最后一部分连接。”…确实,如果您的代码是“bash -c“ true foo $ @ bar baz”,那么以“test.sh one two”运行它将得到“bash -c'true foo one' 'two bar baz'”。 - FeRD$*
的信息,我好像忘记它的存在了。 - ColinM$@
刚刚开始流行,我仍然需要提醒自己它的存在。在脚本中常见的是使用"$*"
...然后作者会意识到它将所有参数都粘在一起,于是他们会尝试各种复杂的无用操作,如单词拆分"$*"
或通过循环shift
逐个拉下来重新组装参数列表...只需使用$@
即可解决问题。(bash使用相同的助记符${var[*]}
访问数组成员,${var[@]}
表示单词列表。) - FeRDbash -c
。 - tripleee我的SUN Unix有很多限制,甚至"$@"不能按预期解释。我的解决方法是${@}。例如:
#!/bin/ksh
find ./ -type f | xargs grep "${@}"
ksh
。 - tripleee--flag
)。$ bar --flag "$1" --flag "$2" --flag "$3"
$ bar $(printf -- ' --flag "%s"' "$@")
注意:为避免额外的字段分割,您必须引用%s
和$@
, 为避免成为单个字符串, 您不能引用printf
的子shell。
"
时,这种方法不起作用 - 您真的需要使用支持 %q
的 printf
。 - Toby Speightbar "$@"
相当于 bar "$1" "$2" "$3" "$4"
请注意引号的重要性!
"$@"
、$@
、"$*"
或 $*
在转义和拼接方面的行为略有不同,详情请参见这个stackoverflow答案。
一个相关的使用案例是将所有给定的参数传递到一个参数中,像这样:
bash -c "bar \"$1\" \"$2\" \"$3\" \"$4\""
。
我使用了 @kvantour 答案的一种变体方法来实现这个目的:
bash -c "bar $(printf -- '"%s" ' "$@")"