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

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

如何在 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
    }
}}'

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

谢谢。

答案1

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

$ mawk 'BEGIN { print "\."; }'
\.
$ gawk 'BEGIN { print "\."; }'
gawk: cmd. line:1: warning: escape sequence `\.' treated as plain `.'
. 

也就是说,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 文本还注意到转义的双重处理:

如果右侧操作数[~!~]是除词法标记 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

就像这样:

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

答案2

从表中可以看出这里,在 awk 的正则表达式中,反斜杠后面最多不跟 3 个八进制数字,另一个反斜杠或任何一个["/abfnrtv]都是未定义的。

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

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

答案3

使用适合工作的正确工具。你有这两个表达式:

$i ~ "string"
$NF ~ "\.pdf"

但在这两种情况下,模式都是文字字符串。因此,没有理由费心使用正则表达式匹配,只需使用文字字符串匹配即可:

index($i, "string")
index($NF, ".pdf")

http://pubs.opengroup.org/onlinepubs/9699919799/utilities/awk.html#tag_20_06_13_13

答案4

与许多其他语言一样,\x在字符串或正则表达式中具有不同的含义。您可以使用

$NF ~ /\.pdf/

或者

$NF ~ "\\.pdf"

字符串"\.pdf"只是一种奇怪的表达方式".pdf"

相关内容