0

我正在学习 awk 编程语言,但我在这里遇到了一个问题。

我有一个文件(awk.dat),具有以下内容:

Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Maecenas pellentesque erat vel tortor consectetur condimentum.
Nunc enim orci, euismod id nisi eget, interdum cursus ex.
Curabitur a dapibus tellus.
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Aliquam interdum mauris volutpat nisl placerat, et facilisis.

我正在使用以下命令:

awk 'BEGIN{RS="*, *";ORS="<<<---\n"} {print $0}' awk.dat

它向我返回了错误:

awk: run time error: regular expression compile failed (missing operand)
*, *
    FILENAME="" FNR=0 NR=0

同时,如果我使用命令:awk 'BEGIN{RS=" *, *";ORS="<<<---\n"} {print $0}' awk.dat,它会给我所需的结果。

我需要了解这部分:RS=" *, *",双引号和*before之间的空格的含义,,因此它会引发错误。

预期输出:

Lorem ipsum dolor sit amet<<<---
consectetur adipiscing elit.
Maecenas pellentesque erat vel tortor consectetur condimentum.
Nunc enim orci<<<---
euismod id nisi eget<<<---
interdum cursus ex.
Curabitur a dapibus tellus.
Lorem ipsum dolor sit amet<<<---
consectetur adipiscing elit.
Aliquam interdum mauris volutpat nisl placerat<<<---
et facilisis.
<<<---

谢谢。

4

3 回答 3

3
"[space1]*,[space2]*"

是一个正则表达式,它匹配字符串:

零个或多个空格 (space1) 后跟一个逗号,然后是零个或多个空格 (space2)

第一个"*,[space]*"是错误的,因为*在正则表达式中有特殊含义。这意味着重复匹配的组/字符零次或多次。你不能把它放在最开始。

于 2018-12-04T16:33:02.787 回答
1

请注意,根据 POSIX,RS它被定义为单个字符而不是正则表达式。

字符串值的第一个字符RS应为输入记录分隔符;<newline> 默认情况下。如果RS包含多个字符,则结果未指定。如果RS为 null,则记录由 <newline> 加上一个或多个空行组成的序列分隔,前导或尾随空行不应导致输入的开头或结尾处为空记录,并且 <newline> 应始终是一个字段分隔符,不管 的值FS是什么。

来源:Awk Posix 标准

这意味着RS=" *, *"导致未定义的行为

其他实现 POSIX 扩展的 awk 版本可能对RS代表的含义有不同的方法。例如 GNU awk 和 mawk。两者都实现RS为正则表达式,但两种实现略有不同。<asterisk> 的使用总结如下:

| RS   | awk (posix)  | gawk             | mawk             |
|------+--------------+------------------+------------------|
| "*"  | "<asterisk>" | "<asterisk>"     | "<asterisk>"     |
| "*c" | undefined    | "<asterisk>c"    | undefined        |
| "c*" | undefined    | "","c","ccc",... | "","c","ccc",... |

c is any character

以上应该解释 OP 的错误,因为RS="*, *"它是根据 mawk 的无效正则表达式。

$ echo "abc" | ./mawk '/*c/'
mawk: line 1: regular expression compile failed (missing operand)

GNU awk:GNU awk的手册说明如下:

使用 时gawk的值RS不限于一个字符的字符串。它可以是任何正则表达式(参见Regexp)。(ce) 一般情况下,每条记录在下一个匹配正则表达式的字符串处结束;下一条记录从匹配字符串的末尾开始。

来源:GNU awk 手册

为了理解 <asterisk> 在 GNU awk 正则表达式中的用法,我们发现:

<asterisk>*此符号表示前面的正则表达式应根据需要重复多次才能找到匹配项。例如,ph**符号应用于前面的符号并查找一个后跟任意数量的sh的匹配项。仅当不存在s 时,这也匹配。phph

有两个微妙的点可以理解如何*工作。首先,*仅适用于单个前面的正则表达式组件(例如,在 中ph*,它仅适用于h)。要*使其应用于更大的子表达式,请使用括号:(ph)*匹配ph、、、phph等等phphph

其次,*找到尽可能多的重复。如果要匹配的文本是phhhhhhhhhhhhhhooey,则ph*匹配所有的hs。

来源:GNU 正则表达式运算符

然而,必须提到的是:

POSIX awk和 gawk*+?运算符代表它们自己,当它们之前的正则表达式中没有任何内容时。例如,/+/匹配文字加号。但是,许多其他版本的 awk 将这种用法视为语法错误。

来源:GNU 正则表达式运算符

因此,设置RS="*, *", 意味着它将匹配字符串"*,", "*, ", "*, ", ...

$ echo "a,b, c" | awk 'BEGIN{RS="*, *"}1'
a,b, c
$ echo "a*,b, c" | awk 'BEGIN{RS="*, *"}1'
a
b, c

mawk: GNU awk 的手册说明如下:

12.多行记录
由于mawk解释RS为正则表达式,多行记录很容易。

来源:man mawk

11. 拆分字符串、记录和文件
awk 程序使用相同的算法将字符串拆分为数组 split(),将记录拆分为字段FSmawk 使用基本相同的算法将文件拆分为RS.

Split(expr,A,sep)工作方式如下:

  1. <剪辑>
  2. 如果sep = " "(单个空格),则 <SPACE> 被从 的前后修剪exprsep变成 <SPACE>。mawk 将 <SPACE> 定义为正则表达式/[ \t\n]+/。否则sep被视为正则表达式,除了长度为 1 的字符串的元字符被忽略,例如, split(x, A, "*")并且split(x, A, /\*/)是相同的。
  3. <剪辑>

来源:man mawk

该手册没有提到应如何解释以元字符开头的正则表达式(例如“*c”)


注意:在 GNU awk 部分中,我通过了 POSIX awk,因为根据 POSIX,形式的正则表达式"*, "会导致未定义的行为。(这与定义无关,RS反正RS不是 POSIX awk 中的 ERE)

awk 实用程序应使用扩展正则表达式表示法(请参阅 XBD扩展正则表达式

来源:Awk Posix 标准

*+?{ <asterisk>、<plus-sign>、<question-mark> 和 <left-brace> 应该是特殊的,除非在括号表达式中使用(参见 RE 括号表达式)。以下任何用途都会产生未定义的结果:

  • 如果这些字符首先出现在 ERE中,或者紧跟在未转义的 <vertical-line>、<circumflex>、<dollar-sign> 或 <left-parenthesis> 之后
  • 如果 <left-brace> 不是有效区间表达式的一部分(请参阅 EREs Matching Multiple Characters)

来源:POSIX 扩展正则表达式

于 2018-12-04T16:49:07.687 回答
1

请您尝试关注一次。

awk '{gsub(", ","<<<---" ORS)} 1;END{print "<<<---"}'   Input_file
于 2018-12-04T17:35:42.977 回答