
次のテキストを含む完全なファイルのリストがあります。
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列を作成します。
サンプルデータを含むファイルに対して2つのテストを実行します。
$ 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
2番目の列のすべての行は、結果をパイピングして削除できます(元のデータの一部をフィルタリングするため)。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言語ファミリーはテキスト処理として最もよく知られています。上記の「1行」Rakuコードは、fff
「トリガー」演算子を使用して2つのセンチネル行間のテキストをキャプチャします。最初は「ユーザー名」行と一致し、2番目は「タイムスタンプ」行と一致します。<!after Event\->
正規表現が「イベント - タイムスタンプ」行を誤って識別しないようにするには、ネガティブナビゲーションが使用されます。
選択した行がプッシュされ、@array
目的split()
の値の左側にあるすべての項目を削除するために使用されます。レコードはbatch()
8つのグループ(列)で構成され、値はmap()
コンマを使用して呼び出されますjoin()
。連続するレコード行は改行文字で連結されます。
2番目の列のCSV行を削除するには、上記の単Start
一行を次のようにパイプします。
raku -ne '.put unless .split(",")[1] eq "Start";'
CSVがコンピューティングライフの主要部分になるためには、ここから始めることをお勧めします。 Rakuには、CSV
より複雑なCSVケースを処理するのに役立つ多くのモジュールがあります。