Awk
awk:欄位上的精確字元串匹配不使用 NUL 作為記錄分隔符
給定一個欄位中有換行符的文件(用雙引號嵌入),我嘗試使用 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