【问题标题】:Awk: gsub("\\\\", "\\\\") yields suprising resultsawk: gsub("\\\\", "\\\\") 产生令人惊讶的结果
【发布时间】:2020-11-18 21:17:33
【问题描述】:

考虑以下输入:

$ cat a
d:\

$ cat a.awk
{ sub("\\", "\\\\"); print $0 }

$ cat a_double.awk
{ sub("\\\\", "\\\\"); print $0 }

现在运行 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 escaping gsub


    【解决方案1】:

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

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

    您的第一种情况没有发生,因此没有匹配,因此没有替换,在您的第二个 awk 脚本中,您正在执行此操作(在 sub 的正则表达式匹配部分中转义部分),因此它完美匹配 \


    让我们通过示例来看看这个,并尝试将... 用于检查目的。

    什么都没有发生时:因为没有匹配到

    awk '{sub("\\", "....\\\\"); print $0}' Input_file
    d:\
    

    发生模式匹配时:

    awk '{sub("\\\\", "...\\\\"); print $0}' 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
    

    【讨论】:

      【解决方案2】:

      *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 $0 }
      
      $ awk -f a.awk a
      awk: a.awk:1: (FILENAME=a FNR=1) fatal: invalid regexp: Trailing backslash: /\/
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-01-09
        • 2022-06-11
        • 2013-06-29
        • 1970-01-01
        • 1970-01-01
        • 2018-12-05
        • 2013-07-14
        • 1970-01-01
        相关资源
        最近更新 更多