在 awk 字段分隔符中使用受保护的通配符不起作用

Using protected wildcard character in awk field separator doesn't work

我有一个文件,其中包含由 *(任意数量)行分隔的段落。当我将 egrep 与 '^\*+$' 的正则表达式一起使用时,它按预期工作,仅显示仅包含星号的行。

但是,当我在 awk -F 或 awk FS 中使用相同的表达式时,它不起作用,只是打印出整个文档,不包括星号行。

到目前为止我尝试过的命令:

awk -F'^\*+$' '{print , }' msgs
awk -F'/^\*+$/' '{print , }' msgs
awk 'BEGIN{ FS="/^\*+$/" } ; { print , }' msgs

打印第一个字段总是打印出整个文档,使用第一个版本它排除了带星号的行,其他版本包括文件中的所有内容。

示例输入:

Par1 test teststsdsfsfdsf
fdsfdsfdsftesyt
fdsfdsfdsf fddsteste345sdfs
***
Par2 dsadawe232343a5edsfe
43s4esfsd s45s45e4t rfgsd45
***
Par3 dsadasd
fasfasf53sdf sfdsf s45 sdfs
dfsf dsf
***
Par4 dasdasda r3ar d afa fs
ds fgdsfgsdfaser ar53d f
***
Par 5 dasdawr3r35a
fsada35awfds46 s46 sdfsds5 34sdf
***

打印 $1 的预期输出:

Par1 test teststsdsfsfdsf fdsfdsfdsftesyt fdsfdsfdsf fddsteste345sdfs

编辑:添加示例输入和预期输出

awk 中用作正则表达式的字符串被解析两次:

  1. 将它们变成正则表达式,并且
  2. 将它们用作正则表达式。

因此,如果您想将字符串用作正则表达式(包括您将字段分隔符或记录分隔符指定为正则表达式的任何时间),那么您需要将任何转义符加倍,因为解析的每次迭代都会消耗其中一个。有关详细信息,请参阅 https://www.gnu.org/software/gawk/manual/gawk.html#Computed-Regexps

好(literal/constant 正则表达式):

$ echo 'a(b)c' | awk '[=10=] ~ /\(b)/'
a(b)c

不好(写得不好的 dynamic/computed 正则表达式):

$ echo 'a(b)c' | awk '[=11=] ~ "\(b)"'
awk: cmd. line:1: warning: escape sequence `\(' treated as plain `('
a(b)c

好(写得很好的 dynamic/computed 正则表达式):

$ echo 'a(b)c' | awk '[=12=] ~ "\(b)"'
a(b)c

但是恕我直言,如果您必须进行两次转义以生成 char 文字,那么使用方括号表达式会更清楚:

$ echo 'a(b)c' | awk '[=13=] ~ "[(]b)"'
a(b)c

此外,正则表达式中的 ^ 表示“string 的开头”,它仅在所有输入的开头匹配,就像 $只会在所有输出的末尾匹配。 ^ 不是 的意思是“ 的开始”,某些 documents/scripts 可能会让您相信。它似乎只意味着在 grep 和 sed 中,因为它们是面向行的,所以通常脚本一次与 1 行进行比较,但 awk 不是面向行的,它是面向记录的,所以输入与regexp 不一定只是一行(如果您将多行读入其保留 space),在 sed 中也是如此。

因此,假设您使用的是 gawk 或其他可以将多字符 RS 视为正则表达式的 awk,那么要匹配一行 *s 作为记录分隔符 (RS),您需要写这个正则表达式:

(^|\n)[*]+(\n|$)

但请注意,它还会匹配目标行中第一个 * 之前和最后一个 * 之后的换行符,因此您需要在代码中适当地处理它。

看来这才是你真正想要做的:

$ awk -v RS='(^|\n)[*]+(\n|$)' 'NR==1{=; print}' file
Par1 test teststsdsfsfdsf fdsfdsfdsftesyt fdsfdsfdsf fddsteste345sdfs