【问题标题】:Propagate all arguments in a bash shell script在 bash shell 脚本中传播所有参数
【发布时间】:2011-03-12 13:46:20
【问题描述】:

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

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

foo.sh:

bar $1 $2 $3 $4

如何在不明确指定每个参数的情况下做到这一点?

【问题讨论】:

标签: bash command-line-arguments


【解决方案1】:

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

java com.myserver.Program "$@"

【讨论】:

  • @Amir:不,不适合 csh。为了大家的理智:do not script csh。但我认为$argv:q 可能适用于某些 csh 变体。
  • 谢谢!根据需要,这会传递脚本接收到的同一组参数——而不是一个大参数。双引号是必须的。即使使用包含空格的引用参数也有效。
  • 相关:如果您的 shell 脚本只是充当运行 java 的包装器,请考虑将最后一行 exec java com.myserver.Program "$@" 这导致 bash 执行到 java,而不是等待它完成。因此,您使用的进程槽少了一个。此外,如果父进程(运行您的脚本)正在通过 pid 监视它,并期望它是“java”进程,那么如果您不执行 exec,一些不寻常的事情可能会中断; exec 导致 java 继承相同的 pid。
  • @dragonxlwang:如果你的 shell 支持它们,你可以使用数组变量(例如 bashzsh 等,但不是普通的 Bourne-或 POSIX-shell):使用 args=("$@") 保存到 args 并使用 "${args[@]}" 将每个元素扩展为单独的 shell “单词”(类似于 "$@")。
  • 在使用"$@" 时是否有任何“陷阱”需要牢记,比如如果您在参数中转义了空格、空字符或其他特殊字符,它会失败吗?
【解决方案2】:

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

[...] , bash 具有 "$@" 变量,它扩展到所有以空格分隔的命令行参数。

来自Bash by example

【讨论】:

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

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

观察:

$ 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 脚本可以看到带有引号或转义字符串的真正原始命令行?
  • 传递标志呢?例如,'./bar.sh --with-stuff'
  • 我建议任何想更好地理解分词主题的人,read more about it here.
  • 我建议您也展示在三个示例中包含'arg with spaces' 时会发生什么。我对结果感到惊讶;希望你能解释一下。
  • @MichaelScheper,无论如何,./foo.sh "arg with spaces"./foo.sh 'arg with spaces' 是 100% 相同的,所以我看不出将它添加到给出的示例中的建议会有什么帮助。
【解决方案4】:
#!/usr/bin/env bash
while [ "$1" != "" ]; do
  echo "Received: ${1}" && shift;
done;

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

【讨论】:

  • 这绝对没有帮助回答他的问题,但这确实很有用。赞成!
  • 传递一个空参数时会中断。你应该检查$#
  • 优秀的 args 测试员,同意 ""'' 作为参数,如果没有 args,它也是无声的。我试图解决这个问题,但需要一个带有$# 的 for 循环和计数器。我只是在最后添加了这个:echo "End of args or received quoted null"
【解决方案5】:

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

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

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

【讨论】:

  • 这是一个 Bash 问题;你正在使用ksh
【解决方案6】:

工作正常,除非您有空格或转义字符。在这种情况下,我找不到捕获参数并发送到脚本内的 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 '@' \' )

【讨论】:

    【解决方案7】:

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

    测试脚本内容:

    # 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
    =================================
    

    【讨论】:

      【解决方案8】:

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

      例子:

      #!/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 中 "$@" 的语义中是有意义的。它还有助于说明$@$* 之间的主要区别,以及它们为何有用。来自bash(1) 手册页的特殊参数部分:“* — 当扩展发生在双引号内时,它会扩展为具有每个参数值的单个单词 [...] 即,"$*" 相当于 @987654332 @,其中c 是 [$IFS]。”事实上,在第一个示例中使用 $* 而不是 $@ 会得到与第二个版本相同的输出。
      • 现在,将其与"$@" 进行比较。再次来自手册页:“@ — 当扩展发生在双引号内时,每个参数扩展为一个单独的单词。也就是说,"$@" 相当于 "$1" "$2" ... 如果发生双引号扩展在一个词中,第一个参数的扩展与原词的开头部分连接,最后一个参数的扩展与原词的最后部分连接。” ...确实,如果您的代码是bash -c "true foo $@ bar baz",那么以test.sh one two 运行它会得到bash -c 'true foo one' 'two bar baz'
      • 感谢 $* 的文档引用和信息,我似乎忘记了它的存在..
      • 嘿。我正好相反,$@ 在我第一次开始编写 shell 脚本时才刚刚获得关注,我仍然需要提醒自己它就在那里。在脚本中使用"$*" 是很常见的......然后作者会意识到这是将他们所有的论点粉碎在一起,所以他们会尝试各种复杂的废话与分词"$*",或[re]通过循环 shift 来组装一个 arg 列表,将它们一个一个拉下来......只需使用 $@ 就可以解决它。 (也有助于 bash 使用相同的助记符来访问数组成员:${var[*]} 表示它们都作为一个单词,${var[@]} 表示单词列表。)
      • 这里真正的问题是您使用bash -c 的方式完全没有意义。
      【解决方案9】:

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

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

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

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

      注意:为避免额外的字段拆分,您必须引用%s$@,并且为避免出现单个字符串,您不能引用printf 的子shell。

      【讨论】:

        【解决方案10】:

        bar "$@" 将等同于 bar "$1" "$2" "$3" "$4"

        注意引号很重要!

        "$@"$@"$*"$* 在转义和连接方面的行为会略有不同,如 stackoverflow answer 中所述。

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

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

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

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

        【讨论】:

          【解决方案11】:

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

          +--------+---------------------------+
          | 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 $*
          

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

          【讨论】:

          • 谢谢!简明扼要地解释所有选项。
          【解决方案12】:

          "${array[@]}" 是在 bash 中传递任何数组的正确方法。我想提供一份完整的备忘单:如何准备参数、绕过和处理它们。

          pre.sh -> foo.sh -> bar.sh.

          #!/bin/bash
          
          args=("--a=b c" "--e=f g")
          args+=("--q=w e" "--a=s \"'d'\"")
          
          ./foo.sh "${args[@]}"
          
          #!/bin/bash
          
          ./bar.sh "$@"
          
          #!/bin/bash
          
          echo $1
          echo $2
          echo $3
          echo $4
          

          结果:

          --a=b c
          --e=f g
          --q=w e
          --a=s "'d'"
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多