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: /\/
考虑以下输入:
$ 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: /\/