将 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

假设每条记录的八个字段始终存在,顺序正确,并且不需要进一步处理即可在 CSV 文件中有效(即不需要额外的引用或转义):

sed -n 's/^[^=]*= //p' file | paste -d , - - - - - - - -

这将删除任何不包含任何等号后跟空格的行(或=子字符串不包含该行中第一个等号的行),然后删除第一个等号之后的空格之前的任何文本。

然后,它用于paste创建剩余数据的八个逗号分隔列。

对包含示例数据的文件进行两次测试:

$ 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)去除前导空格
  • 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 要求awk解决方案,但 Perl 语言家族以文本处理而闻名。上面的“one-liner”Raku 代码使用fff“触发器”运算符来捕获两个哨兵行之间的文本,第一行匹配“用户名”行,第二行匹配“时间戳”行。负向后查找<!after Event\->用于确保正则表达式不会错误地识别“Event-Timestamp”行。

选定的行被推入@array,然后split()用于删除所需值左侧的所有内容。记录batch()以 8 组(列)为单位,并map()使用逗号对join() 值进行调用。连续的记录行由换行符连接。

删除Start第二列中的任何 CSV 行可以通过通过以下管道传输上述单行来完成:

raku -ne '.put unless .split(",")[1] eq "Start";'

如果 CSV 将成为您计算生活的主要部分,那么这是一个很好的起点。 Raku 有许多CSV模块可以帮助您处理更复杂的 CSV 案例。

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

相关内容