【问题标题】:AWK search a file from list in another file and format outputAWK 从另一个文件的列表中搜索文件并格式化输出
【发布时间】:2021-07-21 02:46:52
【问题描述】:

我认为这个问题之前已经回答过,但我一直无法找到满足这一特定需求的问题。

在使用 AWK 搜索约 150mb 的以太网数据包文本文件以查找 csv(或文本)文件中的列表中出现的字符串时,我遇到了困难。其中一个问题似乎是数据文件中的前导空格以及数据文件中冒号后面的信息。

我想在数据文件中搜索 $1 中的术语“Epoch”。这将在每一帧中,然后在列表文件中搜索在 $1 中下一次出现“Epoch”之前找到的任何术语,如果找到它们,则打印 ($3/86400+25569)(将其格式化为工作使用 Excel 时间格式)从“Epoch”行开始,然后是列表文件中的任何匹配项,以逗号 (,) 分隔。

列表文件如下所示

bill_more_data:
and_more_data:
pay_hour:
age_years:
favorite_adventure_type:

数据文件如下所示。

No.     Time           Source                Destination
      1 0.000000       xxx.xx.xxx.x           xxx.xx.xxx.x

Frame 1: 52 bytes on wire (100 bits), 22 bytes captured (757 bits)
    Arrival Time: Jun 28, 2021 04:17:23.747890000 Pacific Daylight Time
    [Time shift for this packet: 0.000000000 seconds]
    Epoch Time: 1624879043.747890000 seconds
    [Time delta from previous captured frame: 0.000000000 seconds]
    Frame Number: 1
 Ethernet II, Src: xxxxxxxxxxxxxx (xxx:xxx:xx:xx:xx:xx:x), Dst: xxx:xxx:xx:xx:xx:xx:x (xxx:xxx:xx:xx:xx:xx:x)
    Destination: xxx:xxx:xx:xx:xx:xx:x (xxx:xxx:xx:xx:xx:xx:x)
        Address: xxx:xxx:xx:xx:xx:xx:x (xxx:xxx:xx:xx:xx:xx:x)
        .... .... .... .... .... .... = LG bit: Locally administered address (this is NOT the factory default)
        .... .... .... .... .... .... = IG bit: Individual address (unicast)
Internet Protocol Version 4, Src: xxx.xx.xxx.x, Dst: 000.00.00.00
    Flags: 0x0, Don't fragment
        0... .... = Reserved bit: Not set
Info Header
    message_id: 000x00
    message_length: 2
bill_some_data
    bill_that_data: 0
    bill_more_data: 1
    and_more_data: 0

0000  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0010  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................

No.     Time           Source                Destination        
      2 0.000275       xxx.xx.xxx.x          xxx.xx.xxx.x          

Frame 2: 60 bytes on wire (454 bits), 55 bytes captured (454 bits)

    Arrival Time: Jun 28, 2021 04:17:23.748165000 Pacific Daylight Time
    [Time shift for this packet: 0.000000000 seconds]
    Epoch Time: 1624879043.748165000 seconds
    [Time delta from previous captured frame: 0.000275000 seconds]
    Frame Number: 2
   Ethernet II, Src: xxx:xxx:xx:xx:xx:xx:x (xxx:xxx:xx:xx:xx:xx:x), Dst: xxx:xxx:xx:xx:xx:xx:x (xxx:xxx:xx:xx:xx:xx:x)
    Destination: xxx:xxx:xx:xx:xx:xx:x (xxx:xxx:xx:xx:xx:xx:x)
        Address: xxx:xxx:xx:xx:xx:xx:x (xxx:xxx:xx:xx:xx:xx:x)
        .... ..1. .... .... .... .... = LG bit: Locally administered address (this is NOT the factory default)
        .... ...0 .... .... .... .... = IG bit: Individual address (unicast)
Internet Protocol Version 4, Src: 172.28.1.72, Dst: 172.28.1.30
    0100 .... = Version: 4
    .... 0101 = Header Length: 20 bytes (5)
    Flags: 0x00
        0... .... = Reserved bit: Not set
        .0.. .... = Don't fragment: Not set
        ..0. .... = More fragments: Not set
    Fragment Offset: 0
    [Stream index: 1]
Info Header
    message_id: 0x00000
    message_length: 5
TED_name
    pay_hour: 3.25
    vacation_days: 0
    age_years: 22
    time_in_role: 0.1
    favorite_adventure_type: excellent

我尝试了以下方法,但没有得到任何输出。如果我更改列表文件以包含冒号后的信息,那么它至少会打印这些行,但这对我没有多大好处。

awk 'NR==FNR{a[$1]++;next}a[$1]' list.txt data.txt

期望的输出

44375.4704137487,bill_more_data,1,bill_more_data,1
1624879043.748165000,pay_hour,3.25,age_years,22,favorite_adventure_type,excellent

或更好地使用格式化为在 Excel 中工作的纪元时间

44375.4704137487,bill_more_data,1,bill_more_data,1
44375.4704137518,pay_hour,3.25,age_years,22,favorite_adventure_type,excellent

我尝试了其他几种方法,但效果有限。我正在寻找一种优雅的方式来做到这一点,它还能够以记录这些值的方式处理未来对格式的更改。我尝试的其他方法之一是运行几个 AWK 命令首先反转文件,然后搜索列表项并打印它们,然后搜索下一个出现或“Epoch”并打印 $3,然后再次反转文件,然后另一个 AWK 打印任何不是“Epoch”的行。如果我直接在命令本身中输入列表项,这似乎工作正常,但如果我试图从另一个文件中读取,我无法让它工作。

在此先感谢您提供任何帮助。这是我第一次使用 AWK,所以如果我问了一个愚蠢的问题,请原谅我。

【问题讨论】:

  • 您尝试的根本问题是它一次检查一行。您希望将第二个文件拆分为多行记录,然后在每个记录中搜索您的字符串。我猜每条记录的第一行是No. Time Source 行,因此您可以将其提供为RS。 (空间在 cmets 中被压缩,但我相信你可以猜到我在看什么。)
  • 很好的第一个问题 - 明确的要求、简洁、可测试的样本输入和预期输出,并尝试自己解决问题!

标签: awk


【解决方案1】:
$ cat tst.awk
BEGIN { OFS="," }
NR==FNR {
    tags[$0]
    next
}
$1 == "Epoch" {
    prt()
    time = $3
}
$1 in tags {
    tag2val[$1] = $2
}
END {
    prt()
}

function prt(   out) {
    if ( time != "" ) {
        out = sprintf("%0.10f",(time / 86400) + 25569)
        for (tag in tag2val) {
            val = tag2val[tag]
            sub(/:/,"",tag)
            out = out OFS tag OFS val
        }
        print out
    }
    delete tag2val
}

$ awk -f tst.awk list file
44375.4704137487,and_more_data,0,bill_more_data,1
44375.4704137519,favorite_adventure_type,excellent,age_years,22,pay_hour,3.25

【讨论】:

  • 谢谢 Ed,这是一段很棒的代码。当 FNR 超过 20000 时,我确实遇到了一个问题。它将开始打印包含“Epoch”的任何行,之后什么都没有。 20k 行是 AWK 与 MAX_LONG 的限制吗?我找不到太多这方面的信息。有针对这个的解决方法吗?在 Tripleee 上面的评论中,提到了为每一帧创建多行记录。我正在处理的文件大约是 19k 帧,因此这可能会起作用,但是当我尝试使用它时,它比您的代码慢得多。
  • @isaac - 不客气。不,没有这样的限制。如果不是打印包含那些其他标签的行,那么它们在输入中不存在(至少与它们在您提供的示例输入中存在的方式不同,例如,它们可能没有与其余部分用空格分隔行,或者您可能缺少换行符或其他内容)。在您的输入文件中查找您认为应该打印其值但没有打印的标签的情况,并查看该行与打印它的其他行有什么不同。
  • 要明确一点 - 问题在于您的输入文件,而不是脚本或任何限制。
  • 感谢大家抽出宝贵时间帮助我。事实证明,我正在处理两个问题。首先是我发布的示例文件只有 2 帧,并且都有所需的数据,但实际上许多帧都没有任何所需的信息,因此需要跳过它们。我能够通过一些 if 语句和一个递增变量来克服这个问题。也许不是最优雅的方式,但效果很好。
  • 另一个问题是文件编码。我正在将文件作为测试文件从 Windows 中的 Wireshark 导出并将其复制到我的 Windows Ubuntu 终端,但这导致文件包含 ASCII 文本和 CRLF 行终止符,这导致了对齐问题并在不需要的地方创建新行。我能够使用 dos2unix 克服这个问题。这不是理想的解决方案,因为 dos2unix 不是标准的,因此需要灌输。 @Ed Morton,代码中有没有办法将 RS 应用到一个文件?
【解决方案2】:

awk 'NR==FNR{a[$1]++;next}a[$1]' list.txt data.txt

这部分是对的:

NR==FNR{a[$1]++;next}

这意味着在list.txt 的末尾,数组a 保存了该文件中的字符串。但对于其余部分,您必须遍历该数组以查看匹配的内容。比如:

/Epoch:/ {
    if( epoch ) { printf "\n" }
    epoch = 1
    printf "%s" $2
}
epoch != 1 { next }

for( term in a ) {
    if( $0 ~ term ) { printf ... }
}

END { if( epoch ) { print "\n" } }

在每个纪元之后打印一个新行:在除第一个以外的每个纪元之前,如果有的话,在末尾。

【讨论】:

    【解决方案3】:

    抱歉回复晚了。 @Ed Morton,感谢您对此的帮助。脚本运行良好!

    只需要稍作修改,用 if 语句过滤掉空白时间戳行,并添加 { sub(/\r$/,"") } 用于回车问题。

    { sub(/\r$/,"") }
    BEGIN { OFS=","}
    NR==FNR {
        tags[$0]
        next
    }
    $1 == "Epoch" {
        prt()
        time = $3
    }
    $1 in tags {
        tag2val[$1] = $2
    }
    END {
        prt()
    }
    
    function prt(   out) {
        if ( time != "" ) {
            out = sprintf("%0.10f",(time / 86400) + 25569)
            for (tag in tag2val) {
                val = tag2val[tag]
                sub(/:/,"",tag)
                out = out OFS tag OFS val
                i = i + 1
           }
        if  (i !=x )
        {
            print out
        }
    x = i
        }
        delete tag2val
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-03-05
      • 2019-05-06
      • 2020-10-02
      • 1970-01-01
      • 2023-03-10
      • 1970-01-01
      • 2018-05-31
      • 1970-01-01
      相关资源
      最近更新 更多