【问题标题】:Which characters need to be escaped when using Bash?使用 Bash 时需要转义哪些字符?
【发布时间】:2013-03-24 21:41:23
【问题描述】:

是否有任何需要在 Bash 中转义的完整字符列表?可以只用sed检查吗?

特别是,我正在检查 % 是否需要转义。我试过了

echo "h%h" | sed 's/%/i/g'

并且工作正常,没有转义%。这是否意味着% 不需要转义?这是检查必要性的好方法吗?

还有更笼统的说法:shellbash 中要转义的字符是否相同?

【问题讨论】:

    标签: bash shell unix escaping special-characters


    【解决方案1】:

    在 Bourne 或 POSIX shell 中需要转义的字符与在 Bash 中不同。通常(非常)Bash 是这些 shell 的超集,因此您在 shell 中转义的任何内容都应该在 Bash 中转义。

    一个很好的一般规则是“如果有疑问,请避开它​​”。但是转义某些字符会赋予它们特殊的含义,例如\n。这些在Quotingecho 下的man bash 页面中列出。

    除此之外,转义任何不是字母数字的字符,这样更安全。我不知道一个明确的清单。

    手册页在某个地方列出了它们,但不是在一个地方。学习语言,这是确定的方法。

    引起我注意的是!。这是 Bash(和 csh)中的特殊字符(历史扩展),但在 Korn shell 中不是。即使echo "Hello world!" 也会出现问题。像往常一样使用单引号会删除特殊含义。

    【讨论】:

    • 我特别喜欢一个很好的一般规则是“如果有疑问,请避开它​​” 建议。仍然怀疑使用sed 检查是否足以查看是否必须转义。感谢您的回答!
    • @fedorqui:检查sed 不是必须的,你几乎可以检查任何东西。 sed 不是问题,bash 是。单引号内没有特殊字符(单引号除外),您甚至不能在那里转义字符。 sed 命令通常应该在单引号内,因为 RE 元字符与 shell 元字符有太多重叠,因此不安全。例外情况是嵌入 shell 变量时,必须小心。
    • 检查echo。如果你把你放进去的东西拿出来,它就不需要逃跑了。 :)
    【解决方案2】:

    我猜你说的是 bash 字符串。有不同类型的字符串对转义有不同的要求。例如。单引号字符串与双引号字符串不同。

    最好的参考是 bash 手册的Quoting 部分。

    它解释了哪些字符需要转义。请注意,根据启用的选项(例如历史扩展),某些字符可能需要转义。

    【讨论】:

    • 所以它确认转义是一个没有简单解决方案的丛林,必须检查每个案例。谢谢!
    • @fedorqui 与任何语言一样,有一套规则需要遵守。对于 bash 字符串转义,规则集非常小,如手册中所述。最容易使用的字符串是单引号,因为不需要转义。但是,没有办法在单引号字符串中包含单引号。
    • @fedorqui。它不是丛林。逃避是完全可行的。请参阅我的新帖子。
    • @fedorqui 您不能在单引号字符串中使用单引号,但您可以使用以下内容“转义”它:'text'"'"'more text'
    【解决方案3】:

    有两个简单而安全的规则不仅适用于sh,也适用于bash

    1。将整个字符串放在单引号中

    这适用于除单引号本身之外的所有字符。要转义单引号,请关闭它之前的引号,插入单引号,然后重新打开引号。

    'I'\''m a s@fe $tring which ends in newline
    '
    

    sed 命令:sed -e "s/'/'\\\\''/g; 1s/^/'/; \$s/\$/'/"

    2。用反斜杠转义每个字符

    这适用于除换行符以外的所有字符。对于换行符,请使用单引号或双引号。 仍然必须处理空字符串 - 替换为 ""

    \I\'\m\ \a\ \s\@\f\e\ \$\t\r\i\n\g\ \w\h\i\c\h\ \e\n\d\s\ \i\n\ \n\e\w\l\i\n\e"
    "
    

    sed 命令:sed -e 's/./\\&/g; 1{$s/^$/""/}; 1!s/^/"/; $!s/$/"/'.

    2b。更易读的 2 版本

    有一组简单安全的字符,例如[a-zA-Z0-9,._+:@%/-],可以不转义以使其更具可读性

    I\'m\ a\ s@fe\ \$tring\ which\ ends\ in\ newline"
    "
    

    sed 命令:LC_ALL=C sed -e 's/[^a-zA-Z0-9,._+@%/-]/\\&/g; 1{$s/^$/""/}; 1!s/^/"/; $!s/$/"/'.


    请注意,在 sed 程序中,无法知道输入的最后一行是否以换行字节结尾(除非它为空)。这就是为什么上述两个 sed 命令都假定它没有。您可以手动添加带引号的换行符。

    请注意,shell 变量仅为 POSIX 意义上的文本定义。未定义处理二进制数据。对于重要的实现,除了 NUL 字节之外,二进制工作(因为变量是用 C 字符串实现的,并且打算用作 C 字符串,即程序参数),但是您应该切换到“二进制”语言环境,例如 latin1 .


    (您可以通过阅读sh 的 POSIX 规范轻松验证规则。对于 bash,请查看 @AustinPhillips 链接的 reference manual

    【讨论】:

    • 注意:#1 的一个很好的变化可以在这里看到:github.com/scop/bash-completion/blob/…。它不需要运行sed,但需要bash
    • 请注意那些努力让这些工作的其他人(比如我!)......看起来你在 OSX 上获得的 sed 的味道并不能正确运行这些 sed 命令。不过它们在 Linux 上运行良好!
    • @dalelane:不能在这里测试。当您有适用于两者的版本时,请进行编辑。
    • 如果字符串以“-”(减号)开头,您似乎错过了,还是仅适用于文件名? - 在后一种情况下,前面需要一个'./'。
    • 我不确定你的意思。使用这些 sed 命令,输入字符串取自标准输入。
    【解决方案4】:

    bash 中避免其他人必须使用 RTFM...:

    用双引号括起来的字符会保留引号内所有字符的文字值,$`\ 以及启用历史扩展时的! 除外。

    ...所以如果你避开那些(当然还有引文本身),你可能没问题。

    如果您采取更保守的“如有疑问,请转义”方法,应该可以通过不转义标识符字符(即 ASCII 字母、数字或“_”)来避免获取具有特殊含义的字符。这些(即在一些奇怪的 POSIX-ish shell 中)不太可能有特殊含义,因此需要转义。

    【讨论】:

    【解决方案5】:

    可作为 shell 输入重复使用的格式

    2021年2月编辑:${var@Q}

    在 bash 下,您可以使用 参数扩展@ 命令来存储变量内容以进行 参数转换

    ${parameter@operator}
           Parameter transformation.  The expansion is either a transforma‐
           tion of the value of parameter or  information  about  parameter
           itself,  depending on the value of operator.  Each operator is a
           single letter:
    
           Q      The expansion is a string that is the value of  parameter
                  quoted in a format that can be reused as input.
    ...
           A      The  expansion  is  a string in the form of an assignment
                  statement or declare command  that,  if  evaluated,  will
                  recreate parameter with its attributes and value.
    

    示例:

    $ var=$'Hello\nGood world.\n'
    $ echo "$var"
    Hello
    Good world.
    
    $ echo "${var@Q}"
    $'Hello\nGood world.\n'
    
    $ echo "${var@A}"
    var=$'Hello\nGood world.\n'
    

    旧答案

    有一个特殊 printf 格式指令 (%q) 为这种请求构建:

    printf [-v var] 格式[参数]

     %q     causes printf to output the corresponding argument
            in a format that can be reused as shell input.
    

    一些示例:

    read foo
    Hello world
    printf "%q\n" "$foo"
    Hello\ world
    
    printf "%q\n" $'Hello world!\n'
    $'Hello world!\n'
    

    这也可以通过变量来使用:

    printf -v var "%q" "$foo
    "
    echo "$var"
    $'Hello world\n'
    

    快速检查所有 (128) 个 ascii 字节:

    请注意,从 128 到 255 的所有字节都必须转义。

    for i in {0..127} ;do
        printf -v var \\%o $i
        printf -v var $var
        printf -v res "%q" "$var"
        esc=E
        [ "$var" = "$res" ] && esc=-
        printf "%02X %s %-7s\n" $i $esc "$res"
    done |
        column
    

    这必须呈现如下内容:

    00 E ''         1A E $'\032'    34 - 4          4E - N          68 - h      
    01 E $'\001'    1B E $'\E'      35 - 5          4F - O          69 - i      
    02 E $'\002'    1C E $'\034'    36 - 6          50 - P          6A - j      
    03 E $'\003'    1D E $'\035'    37 - 7          51 - Q          6B - k      
    04 E $'\004'    1E E $'\036'    38 - 8          52 - R          6C - l      
    05 E $'\005'    1F E $'\037'    39 - 9          53 - S          6D - m      
    06 E $'\006'    20 E \          3A - :          54 - T          6E - n      
    07 E $'\a'      21 E \!         3B E \;         55 - U          6F - o      
    08 E $'\b'      22 E \"         3C E \<         56 - V          70 - p      
    09 E $'\t'      23 E \#         3D - =          57 - W          71 - q      
    0A E $'\n'      24 E \$         3E E \>         58 - X          72 - r      
    0B E $'\v'      25 - %          3F E \?         59 - Y          73 - s      
    0C E $'\f'      26 E \&         40 - @          5A - Z          74 - t      
    0D E $'\r'      27 E \'         41 - A          5B E \[         75 - u      
    0E E $'\016'    28 E \(         42 - B          5C E \\         76 - v      
    0F E $'\017'    29 E \)         43 - C          5D E \]         77 - w      
    10 E $'\020'    2A E \*         44 - D          5E E \^         78 - x      
    11 E $'\021'    2B - +          45 - E          5F - _          79 - y      
    12 E $'\022'    2C E \,         46 - F          60 E \`         7A - z      
    13 E $'\023'    2D - -          47 - G          61 - a          7B E \{     
    14 E $'\024'    2E - .          48 - H          62 - b          7C E \|     
    15 E $'\025'    2F - /          49 - I          63 - c          7D E \}     
    16 E $'\026'    30 - 0          4A - J          64 - d          7E E \~     
    17 E $'\027'    31 - 1          4B - K          65 - e          7F E $'\177'
    18 E $'\030'    32 - 2          4C - L          66 - f      
    19 E $'\031'    33 - 3          4D - M          67 - g      
    

    第一个字段是字节的十六进制值,如果字符需要转义,第二个字段包含E,第三个字段显示字符的转义表示。

    为什么是,

    您可能会看到一些总是需要转义的字符,例如,}{

    所以不是总是,而是有时

    echo test 1, 2, 3 and 4,5.
    test 1, 2, 3 and 4,5.
    

    echo test { 1, 2, 3 }
    test { 1, 2, 3 }
    

    但要注意:

    echo test{1,2,3}
    test1 test2 test3
    
    echo test\ {1,2,3}
    test 1 test 2 test 3
    
    echo test\ {\ 1,\ 2,\ 3\ }
    test  1 test  2 test  3
    
    echo test\ {\ 1\,\ 2,\ 3\ }
    test  1, 2 test  3 
    

    【讨论】:

    • 这有一个问题,通过bash/sh调用pritnf,字符串必须首先为bash/sh进行shell转义
    • @ThorSummoner,如果您将字符串作为文字参数从不同的语言(您可能已经知道如何引用)传递给 shell,则不会。在 Python 中:subprocess.Popen(['bash', '-c', 'printf "%q\0" "$@"', '_', arbitrary_string], stdin=subprocess.PIPE, stdout=subprocess.PIPE).communicate() 将为您提供正确的 shell 引用版本的 arbitrary_string
    • 仅供参考 bash 的 %q 被破坏了很长时间 - 如果我的想法对我有用,那么在被破坏了大约 10 年之后,在 2013 年修复了一个错误(但可能仍然会被破坏)。所以不要依赖它。
    • @CharlesDuffy 当然,一旦你在 Python 领域,shlex.quote() (>= 3.3, pipes.quote() - 未记录 - 对于旧版本) 也将完成这项工作并产生更易于人类阅读大多数字符串的版本(根据需要添加引号和转义),无需生成 shell。
    • 感谢您添加关于, 的特别说明。我惊讶地发现内置 Bash printf -- %q ',' 提供了 \,,但 /usr/bin/printf -- %q ',' 提供了 ,(未转义)。其他字符也一样:{|}~
    【解决方案6】:

    我注意到 bash 在使用自动完成时会自动转义一些字符。

    例如,如果您有一个名为 dir:A 的目录,bash 将自动补全为 dir\:A

    使用它,我使用 ASCII 表的字符进行了一些实验,并得出了以下列表:

    bash 在自动完成时转义的字符:(包括空格)

     !"$&'()*,:;<=>?@[\]^`{|}
    

    bash 不会转义的字符

    #%+-.0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz~
    

    (我排除了/,因为它不能用于目录名称)

    【讨论】:

    • 如果你真的想要一个完整的列表,我建议查看 printf %q 哪些字符在作为参数传递时会修改和不会修改 - 理想情况下,遍历整个字符集。
    • 在某些情况下,即使使用撇号字符串,您也可能希望转义字母和数字以生成特殊字符。例如: tr '\n' '\t' 将换行符转换为制表符。
    • @CharlesDuffy 自动完成转义的字符与printf %q 所做的有些不同,我遇到了这个测试包含'home'波浪号的路径名(%q 转义,导致问题我,自动完成没有)。
    【解决方案7】:

    使用print '%q'technique,我们可以运行一个循环来找出哪些字符是特殊的:

    #!/bin/bash
    special=$'`!@#$%^&*()-_+={}|[]\\;\':",.<>?/ '
    for ((i=0; i < ${#special}; i++)); do
        char="${special:i:1}"
        printf -v q_char '%q' "$char"
        if [[ "$char" != "$q_char" ]]; then
            printf 'Yes - character %s needs to be escaped\n' "$char"
        else
            printf 'No - character %s does not need to be escaped\n' "$char"
        fi
    done | sort
    

    它给出了这个输出:

    No, character % does not need to be escaped
    No, character + does not need to be escaped
    No, character - does not need to be escaped
    No, character . does not need to be escaped
    No, character / does not need to be escaped
    No, character : does not need to be escaped
    No, character = does not need to be escaped
    No, character @ does not need to be escaped
    No, character _ does not need to be escaped
    Yes, character   needs to be escaped
    Yes, character ! needs to be escaped
    Yes, character " needs to be escaped
    Yes, character # needs to be escaped
    Yes, character $ needs to be escaped
    Yes, character & needs to be escaped
    Yes, character ' needs to be escaped
    Yes, character ( needs to be escaped
    Yes, character ) needs to be escaped
    Yes, character * needs to be escaped
    Yes, character , needs to be escaped
    Yes, character ; needs to be escaped
    Yes, character < needs to be escaped
    Yes, character > needs to be escaped
    Yes, character ? needs to be escaped
    Yes, character [ needs to be escaped
    Yes, character \ needs to be escaped
    Yes, character ] needs to be escaped
    Yes, character ^ needs to be escaped
    Yes, character ` needs to be escaped
    Yes, character { needs to be escaped
    Yes, character | needs to be escaped
    Yes, character } needs to be escaped
    

    一些结果,比如, 看起来有点可疑。获得@CharlesDuffy 对此的意见会很有趣。

    【讨论】:

    • 你可以在my answer的最后一段看到,的回答看起来有点可疑
    • 请记住,%q 不知道您打算在 shell 中的哪个位置使用该字符,因此它会转义在任何可能的 shell 上下文中可能具有特殊含义的所有字符。 , 本身对 she shell 没有特殊含义,但正如 @F.Hauri 在回复中指出的那样,它在 {...} 大括号扩展中确实有特殊含义:gnu.org/savannah-checkouts/gnu/bash/manual/… 这就像!这也只需要在特定情况下进行扩展,而不是一般情况:echo Hello World! 工作得很好,但 echo test!test 会失败。
    猜你喜欢
    • 2014-05-14
    • 1970-01-01
    • 2010-11-08
    • 1970-01-01
    • 2018-05-06
    • 1970-01-01
    • 1970-01-01
    • 2018-11-22
    相关资源
    最近更新 更多