为什么括号“][”需要双重转义“\\”以防止shell解释?

αғs*_*нιη 5 command-line bash awk

我使用 'bash' shell 并在文件上执行下面的 'awk' 命令,文件记录用不同字符的数量分隔,如括号、冒号、括号,如下示例记录

...(field#13[field#14:]]:filed#18[filed#19)[...
Run Code Online (Sandbox Code Playgroud)

但是,当我使用单个转义符 '\' 转义“][”时,awk 命令失败并且我必须双重转义 '\\' 括号才能获得预期的结果,它如何需要使用双转义括号(在 'csh ' 壳是一样的)?

awk -F"[\\[\\]:)(]" '{print $18}' inFile
filed#18
Run Code Online (Sandbox Code Playgroud)

还请注意这一点,我知道我可以像下面这样使用双重转义 '\\' 来逃避它们,我只想知道为什么对于括号这是强制性的?

awk -F"[\\[\\]\\:\\)\\(]" '{print $18}' inFile
filed#18
Run Code Online (Sandbox Code Playgroud)

即使使用信号转义也会发出警告(括号除外),但仍然命令执行并得到结果,谢谢

awk -F '[\\[\\]\:\)\(]' '{print $18}' inFile
awk: warning: escape sequence `\:' treated as plain `:'
awk: warning: escape sequence `\)' treated as plain `)'
awk: warning: escape sequence `\(' treated as plain `('
filed#18
Run Code Online (Sandbox Code Playgroud)

ter*_*don 5

这里发生了多个级别的引用/转义。首先,您的FS正则表达式 ( -F "[\\[\\]\:\)\(]") 用双引号引起来。这就是发出警告的原因:

$ awk -F"[\\[\\]:)(]" '{print $2}' file 
awk: warning: escape sequence `\[' treated as plain `['
awk: warning: escape sequence `\]' treated as plain `]'
awk: fatal: :, [., or [=: /[[]:)(]/
Run Code Online (Sandbox Code Playgroud)

虽然单引号可以工作:

$ awk -F'[\\[\\]:)(]' '{print $2}' file 
field#13
Run Code Online (Sandbox Code Playgroud)

这是因为双引号中的任何内容都会首先由 shell 展开。因此,shell 首先扩展\\[\[,然后将其传递给 awk。您可以通过以下方式看到这种情况的发生set -x

$ set -x
$ awk -F"[\\[\\]:)(]" '{print $2}' file 
+ awk '-F[\[\]:)(]' '{print $2}' file
Run Code Online (Sandbox Code Playgroud)

正如你在上面看到的,炮弹吃掉了第一次逃逸。"所以根本不要在这里使用。

下一个问题是 awk 本身会将转义解释两次。因为-F可以接受特殊的转义符,例如\t\r,所以它首先会尝试读取\[单个转义字符。由于\[与 相同[(不同于,说哪一个与换行符\n不同),它会给您一条警告消息,解释它被视为.n\n\[[

因此,您需要第一个转义来转义本身\,第二个转义来转义[。换句话说,在 中\\[,第一个\正在转义第二个\,因此awk最终收到的是\[

如果你考虑这样的事情可能会更容易理解:

$ echo -e 'a\tb'
a   b                  ## prints a tab character
$ echo -e 'a\\tb'
a\tb                   ## prints a literal \t
$ echo -e "a\\tb"      
a   b                  ## prints a tab because of the double quotes
$ echo -e "a\\\tb"     
a\tb                   ## 3ple escaping! Prints a literal `\t` again.
Run Code Online (Sandbox Code Playgroud)

上面的最后一个例子是最能说明问题的。由于要回显的字符串位于双引号中,因此它首先由 shell 扩展(吃掉一个\),然后由echo -e(吃掉另一个\)扩展,最后打印为文本\t