Awk:gsub("\\\\", "\\\\") 产生令人惊讶的结果

Awk: gsub("\\\\", "\\\\") yields suprising results

考虑以下输入:

$ cat a
d:\

$ cat a.awk
{ sub("\", "\\"); print [=10=] }

$ cat a_double.awk
{ sub("\\", "\\"); print [=10=] }

现在运行cat a | awk -f a.awk给出

d:\

和运行 cat a | awk -f a_double.awk 给出

d:\

我希望恰恰相反。我该如何解读?

$ awk -V
GNU Awk 4.1.4, API: 1.1 (GNU MPFR 4.0.1, GNU MP 6.1.2)

是的,这是 awk 的预期行为。当您在第一个脚本中使用 运行 sub("\", "\\") 时,在 sub"(双引号)中,因为我们没有使用 / 来匹配我们需要的模式首先转义 \(实际文字字符)然后转义我们使用 \ 所以我们也需要转义它,因此它将变成 \\

\                                          \
|                                            |
|                                            |
first 2 chars are denoting escaping         next 2 chars are denoting actual literal character \

你的第一个案例没有发生,因此没有匹配,所以没有替换,在你的第二个 awk 脚本中你正在这样做(在 sub 的正则表达式匹配部分转义部分)因此它匹配 \ 完美。


让我们通过示例来看一下,并尝试输入 ... 以进行检查。

什么都没发生: 因为

没有匹配
awk '{sub("\", "....\\"); print [=11=]}' Input_file
d:\

当模式匹配发生时:

awk '{sub("\\", "...\\"); print [=12=]}' Input_file
d:...\

来自 man awk:

gsub(r, s [, t])
For each substring matching the regular expression r in the string t,
 substitute the string s, and return the  number  of  substitutions.

我们如何才能执行实际的转义部分(我们只需要在字符前使用 \ 一次)? 请在 sub 的第一部分中的 /../ 中提及您的正则表达式,我们不需要在此处双重转义 \

awk '{sub(/\/,"&\")} 1' Input_file

*sub() 的第一个参数是正则表达式,而不是字符串,因此您应该使用正则表达式 (/.../) 而不是字符串 ("...") 分隔符。前者是按原样使用的文字正则表达式,而后者定义动态(或计算的)正则表达式,强制 awk 解释字符串两次,第一次将字符串转换为正则表达式,第二次将其用作正则表达式,因此转义所需的反斜杠加倍。参见 https://www.gnu.org/software/gawk/manual/gawk.html#Computed-Regexps

在下文中,我们只需要对反斜杠进行一次转义,因为我们使用的是文字而非动态正则表达式:

$ cat a
d:\

$ awk '{sub(/\/,"\\")}1' a
d:\

您的第一个脚本会在更新版本的 gawk (5.1.0) 中产生语法错误,因为动态正则表达式中的 "\" 等同于文字中的 /\/该表达式的反斜杠正在转义最终的正斜杠,这意味着没有最终定界符:

$ cat a.awk
{ sub("\", "\\"); print [=11=] }

$ awk -f a.awk a
awk: a.awk:1: (FILENAME=a FNR=1) fatal: invalid regexp: Trailing backslash: /\/