フィールドに改行(二重引用符で囲む)を含むファイルがある場合は、NULをレコード区切り文字として使用し、目的のレコードを選択しようとします。そのために、行末をNULに変更し、改行で区切られたフィールドを修正しました(完成を使用sed
)。ただし、最初のフィールドawk
と文字列(GNU)の正確な一致は失敗します。興味深いことに、最初のフィールドで文字列パターンの一致が失敗するため、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を導入し、「newline-splits」アクションを修正する("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をジョブとして使用するのはなぜですかRS
?
答え1
sedコマンドは、\n
改行()をNUL(\0
)に変更せずにNUL +改行(\0\n
)に変更します(cat -A
図を参照)。
GNU awkを使用してRSをに設定すると、\0
後続のレコード(および最初のフィールド)の最初の文字が加算され、正確な\n
一致が中断されます。
改行's/\(,"[^,"]*\)\x00/\1/'
分割修正はこれをまったく変更しません。newline",c
以前のレコードにレコードを追加するだけです。
高速で汚れた「解決策」は、単にセットではなくRS
設定することです。しかし、awkが解析できるようにcsvファイルを処理するこの方法は信頼できないため、実際にはより良いものを見つける必要があります。\0\n
\0
最後の例を使用して:
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
答え2
たとえば、MS-Excelからエクスポートされた場合、ファイルにはCRLF行の末尾を持つLFフィールドが含まれることがあります。この場合、gawkで必要なものは次のとおりです。
awk 'BEGIN{RS=ORS="\r\n"; FPAT="[^,]*|(\"[^\"]*\")+"} $1=="a"' file
たとえば(cat -v
CRをsとしてマークするためにのみ使用されます^M
):
$ cat -v file
head1,head2,head3^M
a,b,c^M
b,no a in first field,c^M
a,"with quotes",c^M
a,"with ,",c^M
b,a,1^M
a,"with
newline",c^M
b,1,a^M
$ awk 'BEGIN{RS=ORS="\r\n"; FPAT="[^,]*|(\"[^\"]*\")+"} $1=="a"' file | cat -v
a,b,c^M
a,"with quotes",c^M
a,"with ,",c^M
a,"with
newline",c^M
上記の方法がうまくいかない理由がある場合は、次を参照してください。https://stackoverflow.com/questions/45420535/whats-the-most-robust-way-to-efficiently-parse-csv-using-awkまたは、gawkextlibからgawks CSVパーサー拡張をダウンロード/使用してください。
答え3
混合 sed awk 方法:
$ < file \
sed -e '
s/$/\x00/
s/\(,"[^,"]*\)\x00/\1/
H;1h;$!d;g
s/\x00\n/\x00/g
' |
awk '/^a,/' RS="\x00" -
説明: sed+awk 混合 目的の結果を得るためにコードを少し変更しました。主なアイデアは、sedが常に配置する改行文字を削除することです。したがって、各レコードを処理した後にsedが印刷されるのを防ぎます。次に、eofから改行を削除し、NULをレコード区切り文字として使用して、NULで区切られたデータをawkに渡します。次に、a で始まるレコードを探します。
出力:
a,b,c
a,"with quotes",c
a,"with ,",c
a,"with
newline",c
awkのみとsedのみの方法は次のとおりです。参照フィールド内の参照を2倍にすることに依存します。
純粋なsed方法:
$ sed -Ee ':a
/^(([^"]*"){2})*[^"]*$/!{
$d;N;ba
}
/^a,/!d
' file
純粋なawkメソッド
$ awk -F\" '
!(NF%2){
t = $0; n = NF
while (getline a > 0) {
t = t ORS a
n = n + split(a, _x, FS)
if (!(nf%2)) break
}
$0 = t
}/^a,/
' file