필드에 줄 바꿈(큰따옴표로 포함)이 포함된 파일이 있는 경우 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
이전 레코드에 레코드를 추가할 뿐입니다.
빠르고 더러운 "솔루션"은 그냥 set 가 아닌 로 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 전용 방법은 다음과 같습니다. 참조 필드 내의 참조를 두 배로 늘리는 데 의존합니다.
순수 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