gawk RS仅在^

时间:2017-07-28 18:32:09

标签: regex bash awk sed gawk

假设我有多行记录=作为记录分隔符,但仅当=是行的开头时才会显示:

$ cat file
record 1, field 1
record 1, field 2 with a = in it
record 1, field 3
= record 2, field 1
record 2, field 2 also with a = in it
record 2, field 3
= final record 3, field 1
record 3, field 2

我想将类似于此的文件分隔为^=[ \t]分隔的记录和\n分隔的字段。

我试过了:

$ gawk -v RS="^=[ \t]" -v FS="\n" '{printf "%s\n--- NF=%s, NR=%s ---\n", $0, NF, FNR}' file

但结果是:

record 1, field 1
record 1, field 2 with a = in it
record 1, field 3
= record 2, field 1
record 2, field 2 also with a = in it
record 2, field 3
= final record 3, field 1
record 3, field 2

--- NF=9, NR=1 ---

即,^不起作用,因为我期望它作为行的开头。

我知道我能做到:

$ gawk -v RS="\n=[ \t]" -v FS="\n" '{printf "%s\nNF=%s, NR=%s\n", $0, NF, FNR}'

但感觉就像使用行分隔符的Unix / Windows问题一样。它还有一个额外的\n附加到最终记录

我可以使用sed^=[ \t]替换为额外\n,然后在段落模式中使用gawk

$ sed 's/^=[ \t]/\
/' file | gawk -v RS="" -v FS="\n" '{printf "%s\n--- NF=%s, NR=%s ---\n", $0, NF, FNR}'
record 1, field 1
record 1, field 2 with a = in it
record 1, field 3
--- NF=3, NR=1 ---
record 2, field 1
record 2, field 2 also with a = in it
record 2, field 3
--- NF=3, NR=2 ---
final record 3, field 1
record 3, field 2
--- NF=2, NR=3 ---

正是正在寻找的东西。

问题:有没有办法在^中使用RS来表示该行的开头行列'在gawk中使用多行记录,所以我不必管道sed?我想我正在寻找m中PCRE正则表达式中gawk标志的等效标记。

3 个答案:

答案 0 :(得分:3)

^表示start of string,而不是start of line。没有start of line字符,只有回车符(\r =将光标返回到行的开头)和换行符(\n =将光标放到下一行)字符根据工具/操作系统一起或单独使用来表示end of line又名newline。 Windows工具倾向于使用\r\n来表示newline,而UNIX仅使用\n,这就是为什么\n在UNIX中通常被称为newline character的原因。 / p>

许多工具,例如sedgrep(默认情况下为awk)一次只读取1行,因此他们的输入缓冲区一次只包含一行,因此在该上下文中start of stringstart of line相同,这就是为什么您经常听到^称为start of line字符的原因,一般来说,它不是。{1}}。类似地,$end of string字符,而不是end of line字符,因为它经常被引用,但在字符串输入的上下文中使用时可用于表示行尾某个工具正在一次读取/填充一行的缓冲区。

这意味着如果你的工具一次不读一行,那么regexp实际上匹配UNIX文件行开头的字符X

(^|\n)X

并且在一行的末尾是:

X(\n|$)

但请注意,如果存在,那也会匹配/消耗换行符。

在Windows中,将\n更改为\r\n以及两者兼而有之,您可以使用\r?\n,除非您的文件是在Windows上创建的,并且可能包含换行中线,例如从Excel导出的CSV可能看起来像

field1,"field2 part a\nfield2 part b",field3\r\n

\n\r当然是字面意思。在这种情况下,您不希望独立的\n中间字段被误解为换行符。

尝试此操作(由于RS的多字符\s[[:space:]]简写,仅限gawk:

$ awk -v RS='\n(=\\s*|$)' -F'\n' '{printf "%s\n--- NF=%s, NR=%s ---\n", $0, NF, FNR}' file
record 1, field 1
record 1, field 2 with a = in it
record 1, field 3
--- NF=3, NR=1 ---
record 2, field 1
record 2, field 2 also with a = in it
record 2, field 3
--- NF=3, NR=2 ---
final record 3, field 1
record 3, field 2
--- NF=2, NR=3 ---

答案 1 :(得分:1)

您可以通过查看最后一个字段来避免最后一条记录额外的新行

$ awk -F'\n' -v RS='\n=[ \t]' -v OFS='\n' '{NF-=$NF==""; 
                                            print $0, "---NF="NF ", ---NR="FNR}' file
record 1, field 1
record 1, field 2 with a = in it
record 1, field 3
---NF=3, ---NR=1
record 2, field 1
record 2, field 2 also with a = in it
record 2, field 3
---NF=3, ---NR=2
final record 3, field 1
record 3, field 2
---NF=2, ---NR=3

答案 2 :(得分:0)

我不知道它是否有所作为,但我发现在BEGIN条款中更容易做到这一点:

awk 'BEGIN {RS = "\n= "; FS = "\n"} {printf "%s\n--- NF=%s, NR=%s ---\n", $0, NF, FNR}' records

这给出了结果:

record 1, field 1
record 1, field 2 with a = in it
record 1, field 3
--- NF=3, NR=1 ---
record 2, field 1
record 2, field 2 also with a = in it
record 2, field 3
--- NF=3, NR=2 ---
final record 3, field 1
record 3, field 2

--- NF=3, NR=3 ---

不需要任何解释,因为除了稍微重新制定你已经做过的事情之外,它确实没有做任何事情。这看起来怎么样?

^,afaik的问题在于没有"线"本身。有记录。我可能是错的,但我不认为"行的开头"概念在这方面是相关的。 "开始时间"将是,或者"开始记录",尽管后者只是像:

$0 ~ /^chars/

但是,我对这部分awk的内部工作情况了解不多,所以我欢迎接受教育。