【问题标题】:How do I escape the wildcard/asterisk character in bash?如何在 bash 中转义通配符/星号字符?
【发布时间】:2008-09-19 14:01:59
【问题描述】:

例如:

me$ FOO="BAR * BAR"
me$ echo $FOO
BAR file1 file2 file3 file4 BAR

并使用\ 转义字符:

me$ FOO="BAR \* BAR"
me$ echo $FOO
BAR \* BAR

我显然在做一些愚蠢的事情。

如何获得输出BAR * BAR

【问题讨论】:

    标签: bash shell escaping


    【解决方案1】:

    设置$FOO 时引用是不够的。您还需要引用变量引用:

    me$ FOO="BAR * BAR"
    me$ echo "$FOO"
    BAR * BAR
    

    【讨论】:

    【解决方案2】:

    简短回答

    就像其他人所说的那样 - 您应该始终引用变量以防止出现奇怪的行为。所以使用 echo "$foo" 而不是 echo $foo

    长答案

    我确实认为这个例子值得进一步解释,因为实际情况比表面上看起来要多。

    我可以看出您的困惑来自哪里,因为在您运行第一个示例后,您可能会认为 shell 显然在做:

    1. 参数扩展
    2. 文件名扩展

    所以从你的第一个例子开始:

    me$ FOO="BAR * BAR"
    me$ echo $FOO
    

    参数展开后相当于:

    me$ echo BAR * BAR
    

    而文件名扩展后相当于:

    me$ echo BAR file1 file2 file3 file4 BAR
    

    如果你只是在命令行中输入echo BAR * BAR,你会发现它们是等价的。

    所以你可能会想“如果我转义 *,我可以防止文件名扩展”

    所以从你的第二个例子来看:

    me$ FOO="BAR \* BAR"
    me$ echo $FOO
    

    参数展开后应该相当于:

    me$ echo BAR \* BAR
    

    并且文件名扩展后应该相当于:

    me$ echo BAR \* BAR
    

    如果您尝试直接在命令行中输入“echo BAR \* BAR”,它确实会打印“BAR * BAR”,因为转义会阻止文件名扩展。

    那么为什么使用 $foo 不起作用?

    这是因为发生了第三个扩展 - 引号删除。从 bash 手动引用删除是:

    在前面的扩展之后,所有 未引用的字符出现 '\'、''' 和 '"' 没有结果 从上述扩展之一是 删除。

    因此,当您直接在命令行中键入命令时,转义字符不是先前扩展的结果,因此 BASH 在将其发送到 echo 命令之前将其删除,但在第二个示例中,“\ *" 是先前参数扩展的结果,因此不会被删除。结果,echo 接收到 "\*" 并打印出来。

    请注意第一个示例之间的区别 - “*”不包含在将被 Quote Removal 删除的字符中。

    我希望这是有道理的。最后得出相同的结论 - 只需使用引号。我只是想解释一下为什么转义(如果只有参数和文件名扩展在起作用,从逻辑上讲应该起作用)不起作用。

    有关 BASH 扩展的完整说明,请参阅:

    http://www.gnu.org/software/bash/manual/bashref.html#Shell-Expansions

    【讨论】:

    • 很好的答案!现在我不觉得我问了这么愚蠢的问题。 :-)
    • 是否有一些实用程序可以转义特殊字符?
    • “你应该总是引用变量以防止奇怪的行为”——当你想将它们用作字符串时
    • 虽然上面@finnw 的答案确实直接回答了这个问题,但这个答案在解释它的 why 方面要好得多,这有助于推断如何使用 in我们自己的场景会起作用。这是我们需要看到更多的答案。
    【解决方案3】:

    我将在这个旧线程中添加一些内容。

    通常你会使用

    $ echo "$FOO"
    

    但是,即使使用这种语法,我也遇到了问题。考虑以下脚本。

    #!/bin/bash
    curl_opts="-s --noproxy * -O"
    curl $curl_opts "$1"
    

    * 需要逐字传递给curl,但也会出现同样的问题。上面的例子不起作用(它将扩展为当前目录中的文件名),\* 也不起作用。您也不能引用$curl_opts,因为它将被识别为curl 的单个(无效)选项。

    curl: option -s --noproxy * -O: is unknown
    curl: try 'curl --help' or 'curl --manual' for more information
    

    因此,如果应用于全局模式,我建议使用 bash 变量 $GLOBIGNORE 以完全防止文件名扩展,或者使用 set -f 内置标志。

    #!/bin/bash
    GLOBIGNORE="*"
    curl_opts="-s --noproxy * -O"
    curl $curl_opts "$1"  ## no filename expansion
    

    应用于您的原始示例:

    me$ FOO="BAR * BAR"
    
    me$ echo $FOO
    BAR file1 file2 file3 file4 BAR
    
    me$ set -f
    me$ echo $FOO
    BAR * BAR
    
    me$ set +f
    me$ GLOBIGNORE=*
    me$ echo $FOO
    BAR * BAR
    

    【讨论】:

    • 很好的解释,谢谢!我的用例是SELECT * FROM etc.,这是唯一可行的方法。
    • 感谢您提供 set -f 解决方案!
    • GLOBIGNORE="*" 正是我需要的,谢谢!
    【解决方案4】:
    FOO='BAR * BAR'
    echo "$FOO"
    

    【讨论】:

    • 这可行,但不必将第一行的单引号更改为双引号。
    • 不,不是这样,但是当您要包含特殊的 shell 字符并且您不想进行任何替换时,最好习惯使用单引号。
    【解决方案5】:
    echo "$FOO"
    

    【讨论】:

      【解决方案6】:

      养成在命令行上使用printf 而不是echo 的习惯可能是值得的。

      在此示例中,它并没有带来太多好处,但对于更复杂的输出,它可能会更有用。

      FOO="BAR * BAR"
      printf %s "$FOO"
      

      【讨论】:

      • 为什么在地狱? printf 是一个单独的进程(至少,不是内置在 bash 中),并且您演示的 printf 的使用与 echo 相比没有任何好处。
      • printf is 是一个内置的 bash,我在回答中确实说过“在这个例子中,它并没有带来太多好处,但它对于更复杂的输出会更有用.
      【解决方案7】:

      如果您不想为 bash 的奇怪扩展而烦恼,您可以这样做

      me$ FOO="BAR \x2A BAR"   # 2A is hex code for *
      me$ echo -e $FOO
      BAR * BAR
      me$ 
      

      这里解释为什么使用 echo 的 -e 选项让生活更轻松:

      来自这里的人的相关引用:

      SYNOPSIS
         echo [SHORT-OPTION]... [STRING]...
         echo LONG-OPTION
      
      DESCRIPTION
         Echo the STRING(s) to standard output.
      
         -n     do not output the trailing newline
      
         -e     enable interpretation of backslash escapes
      
         -E     disable interpretation of backslash escapes (default)
      
         --help display this help and exit
      
         --version
                output version information and exit
      
         If -e is in effect, the following sequences are recognized:
      
         \\     backslash
      
         ...
      
         \0NNN  byte with octal value NNN (1 to 3 digits)
      
         \xHH   byte with hexadecimal value HH (1 to 2 digits)
      

      对于十六进制代码,您可以查看 man ascii 页面(第一行八进制,第二个十进制,第三个十六进制):

         051   41    29    )                           151   105   69    i
         052   42    2A    *                           152   106   6A    j
         053   43    2B    +                           153   107   6B    k
      

      【讨论】:

        猜你喜欢
        • 2018-11-01
        • 2011-06-11
        • 2013-11-22
        • 1970-01-01
        • 2021-11-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多