![sed, awk 또는 tr을 사용하고 콜론(:)을 구분 기호로 사용하여 각 줄을 CSV 형식으로 분할합니다.](https://linux55.com/image/171323/sed%2C%20awk%20%EB%98%90%EB%8A%94%20tr%EC%9D%84%20%EC%82%AC%EC%9A%A9%ED%95%98%EA%B3%A0%20%EC%BD%9C%EB%A1%A0(%3A)%EC%9D%84%20%EA%B5%AC%EB%B6%84%20%EA%B8%B0%ED%98%B8%EB%A1%9C%20%EC%82%AC%EC%9A%A9%ED%95%98%EC%97%AC%20%EA%B0%81%20%EC%A4%84%EC%9D%84%20CSV%20%ED%98%95%EC%8B%9D%EC%9C%BC%EB%A1%9C%20%EB%B6%84%ED%95%A0%ED%95%A9%EB%8B%88%EB%8B%A4..png)
현재 다음과 같이 하나의 열로 정렬된 거대한 고객 계정 정보 파일이 있습니다. :
각 행을 분할하는 구분 기호로 사용하고 싶습니다 . 그런데 이렇게 할 때 각 행에 대해 분리할 때 새 열을 만들고 각 행 뒤의 데이터를 :
해당 열에 넣고 싶습니다 . 나의 궁극적인 목표는 이를 CSV 형식으로 변환하여 데이터 분석 및/또는 데이터베이스 구축을 위해 어딘가로 가져올 수 있도록 하는 것입니다.
firstName:John
middleName:null
lastName:Doe
companyName:John Doe Corp
suffix:null
primaryEmail:[email protected]
primaryPhone:555.555.5555
secondaryEmail:[email protected]
secondaryPhone:null
또한 이는 고객당 총 행 수가 아닙니다. 고객당 55개의 행이 있습니다.
답변1
perl
모든 데스크탑 또는 서버 Linux 배포판에서 사용 가능한 것을 사용하십시오 .
perl -lne '
BEGIN{$,=","}
($k,$v)=split":",$_,2;
next unless defined $v;
for($k,$v){s/"/""/g,$_=qq{"$_"}if/[$,"]/}
$k=$t{$k}//=$t++;
if(exists$f[$k]){print@f;@f=()}
$f[$k]=$v;
END{print@f;print STDERR sort{$t{$a}<=>$t{$b}}keys%t}
' your_file
전체 파일을 처리한 후 헤더(필드 이름을 포함하는 첫 번째 줄)가 stderr로 인쇄된다는 점을 제외하면 파일을 표준 CSV로 변환해야 합니다. 사용 ... >body 2>hdr
하고 어딘가에 저장할 수 있습니다 cat hdr body > final_file.csv
.
빈 줄 등에 대해서는 특별한 의미가 없습니다. 레코드는 순서에 관계없이 이름이 다른 필드 집합으로 구성된 것으로 간주됩니다.
,
또는 를 포함하는 필드는 "
내부에 배치되며 "..."
내부는 "
두 배로 확장하여 이스케이프됩니다 ""
(CSV 규칙 사용).
$,=","
예를 들어 다음과 같이 변경하여 필드 구분 기호를 조정할 수 있습니다 . $,="|"
(또는 $,="\t"
탭의 경우). 줄을 제거하면 인용 및 이스케이프를 제거할 수 있습니다 for($k,$v){ ... }
.
awk
sed
이 작업은 (in 또는 대신)에서 수행할 수 있습니다. 전체 배열을 한 번에 인쇄할 수 있는 방법이 없고(반복해야 함) 문자열을 분할할 수 없기 tr
때문에 조금 더 복잡해질 뿐입니다. awk
제한된 수의 필드(스킬을 사용해야 함 substr
)
답변2
완전성을 위해 awk
- .
-스크립트 awk
(우리는 이를 이라고 부름 convert_csv.awk
):
#!/bin/awk -f
BEGIN{FS=":";OFS=","}
# Process all non-empty lines
NF>0{
# Check if the "key" part of the line was not yet encountered, both globally
# and for the currently processes record.
# If not encountered globally yet, add to list of headers (=columns).
new_hdr=1; new_key=1;
for (i=1; i<=n_hdrs; i++) {if (hdr[i]==$1) new_hdr=0;}
if (new_hdr) hdr[++n_hdrs]=$1;
for (key in val) {if (key==$1) new_key=0;}
# Once no globally new keys are found, consider the "list of headers" as
# complete and print it as CSV header line.
if (!new_hdr && !hdr_printed)
{
for (i=1;i<=n_hdrs;i++) printf("%s%s", hdr[i], i==n_hdrs?ORS:OFS);
hdr_printed=1;
}
# If the current key was already found in the currently processed record,
# we assume that a new record was started, and print the data collected
# so far before collecting data on the next record.
if (!new_key)
{
for (i=1;i<=n_hdrs;i++) printf("%s%s", val[hdr[i]], i==n_hdrs?ORS:OFS);
delete val;
}
# Associate the "value" part of the line with the "key", perform transformations
# as necessary. Since both the 'gsub()' function used for escaping '"' to '""'
# and the 'index()' function used to localize ',' return non-zero if an occurence
# was found, the sum of both return values being > 0 indicates that the field
# must be quoted.
quote=gsub("\"","\"\"",$2)+index($2,",");
if (quote) $2="\""$2"\"";
val[$1]=$2;
}
# Print the last record. If it was the only record, print the header line, too (this
# is the case if 'new_hdr' is still 'true' at end-of-file).
END {
if (new_hdr)
{
for (i=1;i<=n_hdrs;i++) printf("%s%s", hdr[i], i==n_hdrs?ORS:OFS);
}
for (i=1;i<=n_hdrs;i++) printf("%s%s", val[hdr[i]], i==n_hdrs?ORS:OFS);
}
이 함수는 주석에 설명되어 있지만 기본적으로 고유 키 모음을 찾고 행에서 "이미 발견된" 키를 발견하면 레코드가 완료된 것으로 간주한 다음 레코드를 인쇄하고 다음 레코드를 수집하기 위해 임시 버퍼를 지웁니다. . 또한 필드의 특수 문자에 대한 CSV 표준을 준수하기 위해 @mosvy로 표시된 변환을 적용합니다.
전화해
awk -f convert_csv.awk input.txt