为什么正则表达式 `"\.pdf"` 在 gawk 中匹配 `/.../pdf.../...` 而在 mawk 中不匹配?

Tim*_*Tim 7 awk regular-expression mawk gawk

如何在 lsof 输出中仅提取 pid 列和仅提取路径名列?

awk '{ for (i=9; i<=NF; i++) {
    if ($i ~ "string" && $1 != "wineserv" && $5 == "REG" && $NF ~ "\.pdf") {
        $1=$2=$3=$4=$5=$6=$7=$8=""
        print
    }
}}'
Run Code Online (Sandbox Code Playgroud)

正则表达式在 gawk 中"\.pdf"匹配/.../pdf.../...,但在 mawk 中不匹配。我想知道为什么?

谢谢。

ilk*_*chu 12

我不认为这是关于正则表达式,而是关于如何处理双引号字符串。C 风格的转义(如\n)在 awk 字符串中被解释,并且 gawk 和 mawk 以不同的方式对待无效的转义:

$ mawk 'BEGIN { print "\."; }'
\.
$ gawk 'BEGIN { print "\."; }'
gawk: cmd. line:1: warning: escape sequence `\.' treated as plain `.'
. 
Run Code Online (Sandbox Code Playgroud)

也就是说,mawk 似乎保留了反斜杠原样,而 gawk 将其删除(并抱怨,至少在我的版本中)。因此,实际使用的正则表达式是不同的:在 gawk 中,正则表达式是.pdf,当然匹配/pdf,因为点匹配任何单个字符,而在 mawk 中,正则表达式是\.pdf,点被转义并按字面​​匹配。

GNU awk 的手册明确提到在没有定义反斜杠转义序列的字符之前使用反斜杠是不可移植的(请参阅“常规字符前的反斜杠”框):

如果您在字符串常量中前面列出的字符之一之外的内容之前放置一个反斜杠,POSIX awk 会故意将发生的事情保留为未定义。有两种选择:


去掉反斜杠这就是 BWK awk 和 gawk 所做的。例如,"a\qc"与 相同"aqc"
保留反斜杠
其他一些 awk 实现会这样做。在这样的实现中,输入"a\qc"与输入相同"a\\qc"

我假设您希望在正则表达式中对点进行转义,因此安全的方法是$NF ~ "\\.pdf", 或$NF ~ /\.pdf/(因为使用正则表达式文字/.../,转义不是“双重处理”)。

POSIX文还指出,逃逸的双重处理:

如果右侧操作数[of~!~]是除词法标记 ERE 之外的任何表达式,则表达式的字符串值应解释为扩展的正则表达式,包括上述转义约定。请注意,这些相同的转义约定也应应用于确定字符串文字(词法标记 STRING)的值,因此在此上下文中使用字符串文字时应再次应用

因此,这适用于 gawk 和 mawk:

$ ( echo .pdf; echo /pdf ) |
  awk '{ if ($0 ~ "\\.pdf") print "   match: " $0; else print "no match: " $0; }'
   match: .pdf
no match: /pdf
Run Code Online (Sandbox Code Playgroud)

就像这样:

$ ( echo .pdf; echo /pdf ) |
  awk '{ if ($0 ~ /\.pdf/) print "   match: " $0; else print "no match: " $0; }'
   match: .pdf
no match: /pdf
Run Code Online (Sandbox Code Playgroud)


mos*_*svy 5

正如您从此处的表中看到的,在 awk 的正则表达式中,后跟最多 3 个八进制数字的反斜杠、另一个反斜杠或任何一个["/abfnrtv]都是未定义的。

你最好的选择是写[.]而不是\.如果你想要一个文字..

请注意,在这种情况下,它的mawk行为不符合一般惯例;虽然awk我所知道的所有实现都可以让您在正则表达式文字 ( ) 中转义\., \+,但只会让您在用作正则表达式 ( )的字符串中执行相同操作。\*/foo\.bar/mawk$0~"foo\.bar"