Awk

awk:欄位上的精確字元串匹配不使用 NUL 作為記錄分隔符

  • September 24, 2020

給定一個欄位中有換行符的文件(用雙引號嵌入),我嘗試使用 NUL 作為記錄分隔符,然後選擇所需的記錄。為此,我用 NUL 替換了行尾,然後更正了由換行符分割的欄位(使用完成sed)。但是,將(GNU)中的第一個欄位awk與字元串完全匹配失敗。有趣的是,第一個欄位上的字元串模式匹配失敗,這讓我假設它RS="\x00"已正確應用。

為什麼會失敗?為什麼模式匹配有效?

範例文件input.txt

head1,head2,head3
a,b,c
b,no a in first field,c
a,"with quotes",c
a,"with ,",c
b,a,1
a,"with
newline",c
b,1,a

awk在介紹 NUL 作品之前,通過使用精確字元串進行記錄選擇:

$awk 'BEGIN {FS=OFS=","} {if ($1=="a") print}' input.txt

結果:

a,b,c
a,"with quotes",c
a,"with ,",c
a,"with

引入 NUL 並更正“換行符”的工作(注意"with\n newline"條目):

$sed -e 's/$/\x00/' -e 's/\(,"[^,"]*\)\x00/\1/' input.txt | cat -A

head1,head2,head3^@$
a,b,c^@$
b,no a in first field,c^@$
a,"with quotes",c^@$
a,"with ,",c^@$
b,a,1^@$
a,"with$
newline",c^@$
b,1,a^@$

對欄位 1 使用模式匹配有效(注意"a"在其他欄位中如何失敗,但"head1"匹配):

$sed -e 's/$/\x00/' -e 's/\(,"[^,"]*\)\x00/\1/' input.txt |
awk 'BEGIN {RS=ORS="\x00" ; FS=OFS=","}
    { if ($1~"a") print}' |
cat -A

head1,head2,head3^@$
a,b,c^@$
a,"with quotes",c^@$
a,"with ,",c^@$
a,"with$
newline",c^@

但是:欄位 1 的完全匹配"a"失敗:

sed -e 's/$/\x00/' -e 's/\(,"[^,"]*\)\x00/\1/' input.txt |
awk 'BEGIN {RS=ORS="\x00" ; FS=OFS=","} { if ($1=="a") print}' 

##<no output>##

我哪裡錯了?為什麼在使用 NUL as 之前有效RS

您的 sed 命令不會將換行符 ( \n) 更改為 NUL ( \0),而是更改為 NULs + 換行符 ( \0\n) (如圖cat -A所示)。

當使用 GNU awk 並將 RS 設置為\0時,後續記錄(及其第一個欄位)的第一個字元將是\n,這將破壞您的完全匹配。

並且's/\(,"[^,"]*\)\x00/\1/'換行符更正根本不會改變這一點——它只是將 newline",c記錄附加到前一個記錄。


一個快速而骯髒的“解決方案”是設置RS\0\n而不是\0. 但是這種按摩 csv 文件以便它們可以被 awk 解析的方式是不可靠的,所以你真的應該找到更好的東西。

用你的最後一個例子:

sed -e 's/$/\x00/' -e 's/\(,"[^,"]*\)\x00/\1/' input.txt |
gawk 'BEGIN {RS=ORS="\x00\n" ; FS=OFS=","} { if ($1=="a") print}' | cat -A
a,b,c^@$
a,"with quotes",c^@$
a,"with ,",c^@$
a,"with$
newline",c^@$
sed -e 's/$/\x00/' -e 's/\(,"[^,"]*\)\x00/\1/' input.txt |
gawk 'BEGIN {RS="\x00\n" ; FS=OFS=","} { if ($1=="a") print}'
a,b,c
a,"with quotes",c
a,"with ,",c
a,"with
newline",c

引用自:https://unix.stackexchange.com/questions/610962