AWK コマンドの結果を新しい CSV ファイルに書き込みます。

AWK コマンドの結果を新しい CSV ファイルに書き込みます。

次のテキストを含む完全なファイルのリストがあります。

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

Start2番目の列のすべての行は、結果をパイピングして削除できます(元のデータの一部をフィルタリングするため)。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==22つのフィールドを持つ行を選択
  • substr($2,2)先行スペースを削除
  • sourcedestソースファイルとターゲットファイル。

プログラムを維持するために交換することができます。

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が解決策を必要としたことはわかっていますが、awkPerl言語ファミリーはテキスト処理として最もよく知られています。上記の「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ケースを処理するのに役立つ多くのモジュールがあります。

https://modules.raku.org/search/?q=CSV
https://raku.org/

関連情報