【问题标题】:How to preserve double quotes in $@ in a shell script?如何在 shell 脚本中保留 $@ 中的双引号?
【发布时间】:2010-09-20 21:45:13
【问题描述】:

假设我有一个非常简单的 shell 脚本 'foo':

  #!/bin/sh
  echo $@

如果我这样调用它:

  foo 1 2 3

它愉快地打印:

  1 2 3

但是,假设我的一个论点是用双引号括起来的并且包含空格:

  foo 1 "this arg has whitespace" 3

foo 愉快地打印:

  1 this arg has whitespace 3

双引号已被删除!我知道 shell 认为它帮了我一个忙,但是......我想了解原始版本的论点,不受 shell 解释的干扰。有什么办法吗?

【问题讨论】:

    标签: shell


    【解决方案1】:

    首先,您可能想要引用版本的$@,即"$@"。要感受其中的不同,请尝试在字符串中放置多个空格。

    其次,引号是 shell 语法的元素——它对你没有帮助。为了保护它们,你需要逃避它们。例子:

    foo 1 "\"this arg has whitespace\"" 3
    
    foo 1 '"this arg has whitespace"' 3
    

    【讨论】:

    【解决方案2】:

    双引号 $@:

    #!/bin/sh
    for ARG in "$@"
    do
        echo $ARG
    done
    

    然后:

    foo 1 "this arg has whitespace" 3
    

    会给你:

    1
    this arg has whitespace
    3
    

    【讨论】:

    • 然后可以使用IFS 来避免引用其他答案中展示的地狱,如果想对这些参数做任何有用的事情......
    【解决方案3】:

    我要做的是将收到的所有参数用空格引用,这可能对您的情况有所帮助。

    for x in "${@}" ; do
        # try to figure out if quoting was required for the $x
        if [[ "$x" != "${x%[[:space:]]*}" ]]; then
            x="\""$x"\""
        fi
        echo $x
        _args=$_args" "$x
    done
    
    echo "All Cmd Args are: $_args"
    

    【讨论】:

    • "argument with spaces" 打印完全相同的输入。 "argument" 打印时不带双引号。但这最有效
    【解决方案4】:

    你需要引用引号:

    foo 1 "\"this arg has whitespace\"" 3
    

    或(更简单)

    foo 1 '"this arg has whitespace"' 3
    

    您需要将双引号括起来,以确保 shell 在解析单词参数时不会删除它们。

    【讨论】:

      【解决方案5】:

      假设您的设置更加严格,您不能更改命令行,并通过转义双引号使其更加“友好”。例如:

      example_script.sh argument_without_quotes "argument with quotes i cannot escape"
      

      首先考虑在您的脚本中,您无法判断参数是带引号还是不带引号,因为 shell 会剥离它们。

      所以你可以做的是为包含空格的参数重建双引号

      这个例子重建了整个命令行,双引号包含空格的参数

      #!/bin/sh
      #initialize the variable that will contain the whole argument string
      argList=""
      #iterate on each argument
      for arg in "$@"
      do
        #if an argument contains a white space, enclose it in double quotes and append to the list
        #otherwise simply append the argument to the list
        if echo $arg | grep -q " "; then
         argList="$argList \"$arg\""
        else
         argList="$argList $arg"
        fi
      done
      
      #remove a possible trailing space at the beginning of the list
      argList=$(echo $argList | sed 's/^ *//')
      
      #pass your argument list WITH QUOTES
      echo "my_executable" $argList
      #my_executable $argList
      

      注意这个限制。如果你运行这个例子

      example_script.sh "argument with spaces" argument_without_spaces "argument_doublequoted_but_without_spaces"
      

      你会得到这个输出

      my_executable "argument with spaces" argument_without_spaces argument_doublequoted_but_without_spaces
      

      注意最后一个参数:因为它没有空格,所以它没有再次用双引号括起来,但这应该不是问题。

      【讨论】:

        【解决方案6】:

        我发现的最可靠的方法是利用“xtrace”外壳内置的逻辑。这是一个通过set -x 打开的子系统,它将打印shell 运行的每个命令。如果我们打开 xtrace 然后使用相同的参数运行一个不执行任何操作的命令 (true),shell(大多数 shell...)将为我们引用参数。

        show_quoted() {
          (
            # use > as the xtrace prefix (this is the default)
            PS4='+'
            exec 2>&1  # send the xtrace (on stderr, 2) to stdout (1) for processing
            # turn on xtrace -- all commands are printed, with $PS4 as a prefix
            set -x
            # `true` is the command that does nothing, successfully
            true "$@"
          ) |
            sed '
              # remove the xtrace prefix -- any number of + characters
              s/^+*//
              # hide the "true" command
              s/^true //
            '
        }
        

        同样的事情,但有正常数量的 cmets:

        show_quoted() {
          (
            PS4='+'  # reset to default
            exec 2>&1  # send xtrace to stdout
            set -x
            true "$@"
          ) | sed 's/^+*true //'  # remove the xtrace prefix
        }
        

        这是我的测试结果:(这些有点不同,但都是有效的)

        $ sh show_quoted.sh 1 '2 3' 'he said: "hi"' "she said: 'bye'"
        1 '2 3' 'he said: "hi"' 'she said: '\''bye'\'''
        
        $ bash show_quoted.sh 1 '2 3' 'he said: "hi"' "she said: 'bye'"
        1 '2 3' 'he said: "hi"' 'she said: '\''bye'\'''
        
        $ zsh show_quoted.sh 1 '2 3' 'he said: "hi"' "she said: 'bye'"
        1 '2 3' 'he said: "hi"' 'she said: '\''bye'\'
        
        $ busybox sh show_quoted.sh 1 '2 3' 'he said: "hi"' "she said: 'bye'"
        1 '2 3' 'he said: "hi"' 'she said: '"'"'bye'"'"
        

        然而,这些 shell(下图)并不能胜任这项任务。公平地说,玩具箱外壳“完成了 80%”。

        $ dash show_quoted.sh 1 '2 3' 'he said: "hi"' "she said: 'bye'"
        1 2 3 he said: "hi" she said: 'bye'
        
        $ ./toybox sh show_quoted.sh 1 '2 3' 'he said: "hi"' "she said: 'bye'"
        "$@"
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-09-28
          • 2020-01-26
          • 2011-10-22
          • 2014-07-28
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多