다음 텍스트가 포함된 전체 파일 목록이 있습니다.
Sun Aug 22 19:00:00 2021
User-Name = "407359687"
Acct-Status-Type = Interim-Update
Acct-Output-Octets = 3263901190
Acct-Session-Id = "PPP3092201SSG0001006b0a55AABODS"
Acct-Session-Time = 1146851
Acct-Output-Gigawords = 15
Event-Timestamp = "Aug 22 2021 18:55:32 +08"
Timestamp = 1629630000
내 목표는 중요한 행을 가져와 새 CSV 파일에 저장하는 것입니다. 아래 AWK 명령을 사용하여 텍스트의 값을 정렬하고 있지만 CSV 파일에 쓰는 방법을 모르겠습니다.
awk '{if ($1 == "User-Name")
{start=1; wholeLine=""; wholeLine = wholeLine$3;}
if ($1$2$3 =="Acct-Status-Type=Interim-Update"||$1$2$3 =="Acct-Status-Type=Stop")
{wholeLine=wholeLine","$3;}
else if ($1$2$3 =="Acct-Status-Type=Start")
{start=0;wholeLine=""}
if (($1=="Acct-Output-Octets")&&(start==1))
{wholeLine=wholeLine","$3;}
if (($1=="Acct-Session-Id")&&(start==1))
{wholeLine=wholeLine","$3;}
if (($1=="Acct-Session-Time")&&(start==1))
{wholeLine=wholeLine","$3;}
if (($1=="Acct-Output-Gigawords")&&(start==1))
{wholeLine=wholeLine","$3;}
if (($1=="Event-Timestamp")&&(start==1))
{timeStamp="";timeStamp=$3" "$4" "$5" "$6" "$7;wholeLine=wholeLine","timeStamp}
if (($1=="Timestamp")&&(start==1))
{wholeLine=wholeLine","$3;}
if (($1=="")&&(start==1))
{start=0;print wholeLine}}' /home/file/detail-20210822
내 예상 CVS 결과는 다음과 같아야 합니다.
"405947674",Interim-Update,1079493624,"PPP3082110SSG000100be4a72AAAk5Y",25440,0,"Aug 22 2021 19:00:43 +08",1629630315
답변1
각 레코드의 8개 필드가 항상 올바른 순서로 존재하고 CSV 파일에서 유효하기 위해 추가 처리가 필요하지 않다고 가정합니다(즉, 추가 인용이나 이스케이프가 필요하지 않음).
sed -n 's/^[^=]*= //p' file | paste -d , - - - - - - - -
그러면 등호 뒤에 공백이 오는 줄(또는 =
하위 문자열에 줄의 첫 번째 등호가 포함되지 않은 줄)이 포함되지 않은 줄이 제거되고, 첫 번째 등호 뒤와 공백 앞의 모든 텍스트가 제거됩니다.
paste
그런 다음 나머지 데이터의 쉼표로 구분된 8개 열을 만드는 데 사용됩니다 .
샘플 데이터가 포함된 파일에 대해 두 가지 테스트를 실행합니다.
$ sed -n -e 's/^[^=]*= //p' file | paste -d , - - - - - - - -
"407359687",Interim-Update,3263901190,"PPP3092201SSG0001006b0a55AABODS",1146851,15,"Aug 22 2021 18:55:32 +08",1629630000
"407359687",Interim-Update,3263901190,"PPP3092201SSG0001006b0a55AABODS",1146851,15,"Aug 22 2021 18:55:32 +08",1629630000
Start
두 번째 열의 모든 행은 결과를 파이핑하여 제거할 수 있습니다 (원본 데이터의 일부를 필터링하기 위해).Acct-Status-Type = Start
awk -F , '$2 != "Start"'
답변2
=
레코드를 분리하는 유일한 것은 샘플 입력 상단의 타임스탬프이고 각 레코드에는 모두 동일한 레이블(이름/키/기호 데이터 의 왼쪽)이 포함되어 있다고 가정하여 실제로 파일을 CSV로 변환하는 방법은 다음과 같습니다. :
$ cat tst.awk
BEGIN { OFS="," }
/=/ {
gsub(/^[[:space:]]+|[[:space:]]+$/,"")
tag = val = $0
sub(/[[:space:]]*=.*/,"",tag)
sub(/[^=]*=[[:space:]]*/,"",val)
if ( !(tag in tag2val) ) {
tags[++numTags] = tag
}
tag2val[tag] = val
next
}
NR>1 { prt() }
END { prt() }
function prt( tagNr, tag, val) {
if ( !doneHdr++ ) {
for (tagNr=1; tagNr<=numTags; tagNr++) {
tag = sanitize(tags[tagNr])
printf "%s%s", tag, (tagNr<numTags ? OFS : ORS)
}
}
for (tagNr=1; tagNr<=numTags; tagNr++) {
tag = tags[tagNr]
val = sanitize(tag2val[tag])
printf "%s%s", val, (tagNr<numTags ? OFS : ORS)
}
numTags = 0
delete tag2val
}
function sanitize(inStr, outStr) {
outStr = inStr
if ( outStr ~ ("[" OFS "\"]") ) {
gsub(/^"|"$/,"",outStr)
gsub(/"/,"\"\"",outStr)
outStr = "\"" outStr "\""
}
return outStr
}
$ awk -f tst.awk file
User-Name,Acct-Status-Type,Acct-Output-Octets,Acct-Session-Id,Acct-Session-Time,Acct-Output-Gigawords,Event-Timestamp,Timestamp
"407359687",Interim-Update,3263901190,"PPP3092201SSG0001006b0a55AABODS",1146851,15,"Aug 22 2021 18:55:32 +08",1629630000
이것을 파일에 쓰는 것은 다른 명령 출력을 파일에 쓰는 것과 같습니다.
awk -f tst.awk file > output.csv
위의 내용은 입력 값이나 레이블에 줄 바꿈 외에 =
, "
, ,
s 또는 기타 문자가 포함되어 있어도 정확하고 유효한 CSV를 출력합니다.
헤더 행이 실제로 필요하지 않은 경우 함수에서 if ( !doneHdr++ )
블록을 제거 하면 됩니다 prt()
.
답변3
내가 먼저 할게
awk -F= 'NF==2{printf "%s%s",comma,substr($2,2);comma=","} END {printf "\n" }' source > dest
어디
-F=
=
구분자 로 사용NF==2
2개의 필드가 있는 행 선택substr($2,2)
선행 공백 제거source
dest
소스 및 대상 파일입니다 .
프로그램을 유지하려면 교체할 수 있습니다.
if (($1=="Acct-Session-Id")&&(start==1))
{wholeLine=wholeLine","$3;}
통과
$1 ~ /Acct-Session-Id/ && (start==1) {wholeLine=wholeLine","substr($2,2);}
@berndbausch가 지적했듯이 둘러싸는 { ... } 를 제거하십시오.
답변4
Raku(이전 Perl_6) 사용
raku -e 'my @array; for lines() {@array.push($_) if /User\-Name/ fff /<!after Event\-> Timestamp/};
@array>>.split(/^^ .+? " = "/, :skip-empty).batch(8).map(*.join(",")).join("\n").put;'
입력 예(@FelixJN 이후):
Sun Aug 22 19:00:00 2021
User-Name = "407359687"
Acct-Status-Type = Interim-Update
Acct-Output-Octets = 3263901190
Acct-Session-Id = "PPP3092201SSG0001006b0a55AABODS"
Acct-Session-Time = 1146851
Acct-Output-Gigawords = 15
Event-Timestamp = "Aug 22 2021 18:55:32 +08"
Timestamp = 1629630000
RANDOM ANNOYANCE
AND AN EMPTY LINE
Sun Aug 22 19:00:00 2021
User-Name = "407359687"
Acct-Status-Type = Interim-Update
Acct-Output-Octets = 3263901190
Acct-Session-Id = "PPP3092201SSG0001006b0a55AABODS"
Acct-Session-Time = 1146851
Acct-Output-Gigawords = 15
Event-Timestamp = "Aug 22 2021 18:55:32 +08"
Timestamp = 1629630000
예제 출력:
"407359687",Start,3263901190,"PPP3092201SSG0001006b0a55AABODS",1146851,15,"Aug 22 2021 18:55:32 +08",1629630000
"407359687",Interim-Update,3263901190,"PPP3092201SSG0001006b0a55AABODS",1146851,15,"Aug 22 2021 18:55:32 +08",1629630000
OP가 솔루션을 요구했다는 것을 알고 있지만 awk
Perl 언어 계열은 텍스트 처리로 가장 잘 알려져 있습니다. 위의 "한 줄짜리" Raku 코드는 fff
"트리거" 연산자를 사용하여 두 센티넬 줄 사이의 텍스트를 캡처합니다. 첫 번째는 "사용자 이름" 줄과 일치하고 두 번째는 "타임스탬프" 줄과 일치합니다. <!after Event\->
정규 표현식이 "이벤트-타임스탬프" 줄을 잘못 식별하지 않도록 하기 위해 부정 탐색이 사용됩니다.
선택한 행이 푸시된 @array
다음 split()
원하는 값 왼쪽에 있는 모든 항목을 삭제하는 데 사용됩니다. 레코드는 batch()
8개의 그룹(열)으로 구성되며 값은 map()
쉼표를 사용하여 호출됩니다 join()
. 연속적인 레코드 줄은 개행 문자로 연결됩니다.
두 번째 열의 CSV 행을 제거하려면 Start
다음을 통해 위의 단일 행을 파이프하면 됩니다.
raku -ne '.put unless .split(",")[1] eq "Start";'
CSV가 컴퓨팅 생활의 주요 부분이 되려면 여기에서 시작하는 것이 좋습니다. Raku에는 CSV
더 복잡한 CSV 사례를 처리하는 데 도움이 되는 많은 모듈이 있습니다 .