【问题标题】:How do I pass an empty string with double quotes to a bash script?如何将带有双引号的空字符串传递给 bash 脚本?
【发布时间】:2014-06-11 20:58:15
【问题描述】:

我正在尝试编写一个脚本,该脚本将在命令行上接受一个带双引号的空字符串作为附加参数。如果提供了双引号空字符串,我希望脚本将这些参数与双引号空字符串一起传递,但命令解释器似乎将这样的参数解释为空字符串并去掉引号。有没有办法做到这一点?

作为一个简单的例子,我将在文件 script.sh 中:

#!/bin/bash
/home/myapp $1 $2

如果我在提示符下运行:

$ ./script.sh arg1 ""

脚本只执行"/home/myapp arg1",但错过/忽略了第二个参数("")。我希望它保留这个空字符串,而是执行:/home/myapp arg1 ""

【问题讨论】:

    标签: bash shell


    【解决方案1】:

    您正确地将空字符串参数传递给脚本。

    是脚本搞砸了:

    #!/bin/bash
    /home/myapp $1 $2
    

    脚本没有保护$1$2 的扩展免于分词。这意味着如果 $1$2 包含多个单词,它们就会变成单独的参数,如果它们中的任何一个扩展为空,它们就会消失。

    这应该是:

    #!/bin/bash
    /home/myapp "$1" "$2"
    

    通常,您可以让脚本将其所有参数传递给被调用的程序,如下所示:

    /home/myapp "$@"
    

    引号只是 shell 语法;它们不是参数数据本身的一部分。当您在 shell 中键入 program "" 时,在操作系统级别,程序会收到一个空的 C 语言字符串:一个指向空字节的指针。没有引号。

    您可以传递参数""(由两个双引号组成的两个字符的字符串),但这不是一个空参数。一种方法是,例如,'""':用单引号括起来。

    这样做的唯一原因是当您在元级别操作 shell 语法时:传递 shell 脚本的片段源代码,例如带引号的标记、空的或其他。 shell 有一个名为eval 的命令,它将源代码作为参数(或多个参数)并对其进行评估。

    empty_shell_string_syntax='""'    # this variable holds the characters ""
    
    eval empty_var=$empty_shell_string_syntax   # empty_var gets set to empty string
    

    在调用eval 之前,命令行会被扩展。该扩展删除了语法$empty_shell_string_sytnax 并将其替换为内容,即字符""。然后 eval 得到字符串empty_var=""。它对此进行评估,因此empty_var 被设置为空字符串,如语法所示。

    【讨论】:

    • 你是对的,需要双引号引用参数引用,但如果你无条件引用$2 ("$2"),它将总是作为一个(空)参数,即使没有传入第二个参数,这不是 OP 的意图;相比之下,"$@" 按预期工作; +1 其余的解释。
    • 您可以使用例如解决@mklement0 提到的问题。 /home/myapp ${1+"$1"} ${2+"$2"}。这有点迂回,但它的工作方式是 ${var+something} 构造扩展为 something IF $var 已定义 - 因此如果 $2 未定义,${2+"$2"} 扩展为空并消失;如果 $2 被定义(即使它被定义为空字符串),它会扩展为 "$2" 并且双引号保护它免受分词和 vanishing-if-blank。
    【解决方案2】:

    您可以通过examining the $# parameter 检测到向您的脚本传递了多少位置参数。例如,如果这是 2 或更大,那么您知道为 $2 传递了某些内容,即使 $2 为空(通过调用 shell 删除了引号)。以下实现了该逻辑,如果参数已传递但为空,则将 "" 传递给被调用程序(在本例中为 echo):

    #!/bin/bash
    
    if [ $# -ge 2 ]; then
        if [ "$2" ]; then
            arg2="$2"
        else
            arg2='""'
        fi
        echo "$1 $arg2"
    elif [ $# == 1 ]; then
        echo "$1"
    fi
    

    输出:

    $ ./args.sh 一个 一种 $ ./args.sh 一个“” 一种 ”” $ ./args.sh 一个 1 一个 1 $

    【讨论】:

    • 完美,正是我需要的谢谢!
    • 设置arg2='""' 在你回显它时看起来不错,但是如果你尝试使用它来将一个空白字符串传递给其他东西(如/home/myapp $1 $arg2),它将不起作用--它将传递由两个双引号组成的字符串,而不是空字符串。
    • @GordonDavisson 我希望它保留这个空字符串,而是执行:/home/myapp arg1 "" 这不正是 OP 所要求的吗?
    • @DigitalTrauma,你说得对,这就是我想要的。被调用的程序实际上希望看到包含双引号的“”,并且如果没有传递任何内容或传递不带引号的空字符串,则无法正常工作。这就是我选择你的答案的原因。对于任何感兴趣的人,myapp 确实是比特币客户端,它有一个特定的命令 bitcoind getaddressesbyaccount 。默认帐户始终为“”,因此要使此命令正常工作,引号必须存在。我以非特权用户身份运行客户端,我的脚本使用 sudo 运行带有 -u 标志的命令。
    【解决方案3】:

    补充Kaz's helpful explanation:
    如果你通过"$@"empty参数也将通过

    #!/bin/bash
    
    /home/myapp "$@"
    

    通过"$@" 保留原始参数完全 作为传入 - 甚至作为空字符串 传递的参数(例如,''"""$someVar",其中变量someVar 未设置或包含空字符串。


    如果可能有超过 2 个参数

    #!/bin/bash
    
    # Copy the (up to) first 2 arguments - whether they're empty strings or not -
    # to a separate array.
    firstTwo=("${@:1:2}") 
    shift; shift  # Remove the first 2 arguments from the arguments array.
    
    # Invoke the app with the (up to) first 2 arguments stored in the new array.
    /home/myapp "${firstTwo[@]}"
    
    # Process the remaining arguments, if any.
    for a; do
      echo "remaining: [$a]"
    done
    

    【讨论】:

      【解决方案4】:

      你应该使用反斜杠转义它

      $ ./script.sh arg1 \"\"
      

      可以测试,例如回声:

      $ echo \"\"
      ""
      

      【讨论】:

      • 谢谢,是的,我同意这行得通,有没有办法不要求用户使用一些脚本魔法来转义引号以检测他们输入了这样一个空字符串并用双引号替换命令?
      • @Josh 不知道有这样的魔法。这个解析是由 shell 完成的,所以你必须修改 shell 以应用不同的解析规则。
      【解决方案5】:

      用反斜杠转义它们或将它们放在单引号中。

      【讨论】:

        【解决方案6】:

        您也可以将它们放在单引号内以获取其文字值,例如:

          ]$ echo '""'
          ""
        

        例如,在脚本中:

           ]# cat a.bash
             #!/bin/bash        
             echo a$1b
        
          ]$ ./a.sh '""'
          a""b
        
          ]$ ./a.sh ""
          ab
        

        【讨论】:

          【解决方案7】:

          试试这个:

          cmd=yourcomand;
          n=0; for a in "$@"; do n=$((n +1)); cmd="$cmd \${$n+\"\$$n\"}"; done
          eval $cmd
          

          【讨论】:

          • 虽然此代码可能会回答问题,但提供有关它如何和/或为什么解决问题的额外上下文将提高​​答案的长期价值。
          • 这是基于上面的 Kaz 和 Gordon Davisson 注释,谢谢。但是请注意,通常 ${X+"$X"} 的扩展可能会产生不希望的结果,具体取决于变量值包含的字符。所以最好在命令行上使用所有变量的一般替换,只有在验证存在一个空字符串之后。从上述回复中,我还没有看到通用的解决方案。
          猜你喜欢
          • 2014-04-13
          • 1970-01-01
          • 2018-03-26
          • 1970-01-01
          • 1970-01-01
          • 2017-10-31
          • 2016-12-09
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多