Windows格式大型日志文件过滤需求及Perl代码故障求助
问题描述
需要对Windows格式的大型日志文件执行以下操作(不修改原文件):
- 移除所有CRLF字符(即去掉
\r) - 在日志末尾的
CLG...与TRC...行之间插入空行 - 以段落模式读取处理后的结果,若段落中包含特定Call-ID则打印对应段落,同时打印该段落中含
rtpmap的行
但提供的Perl代码无法正常工作。
原Perl代码
use strict; use warnings; my $ID = "D5CCA1AE-686D11E2-A881ED01-8DFA6D70@10.218.16.2"; my $SDP; open (LOG, "file.log") || die $!; my $line; while(<LOG>) { $line .= $_; $line =~s/\r//g; } local $/ = ''; while (<>) { if ( /Call-ID:\s+(.+)/ and $ID ) { $SDP = 1; print; next; } print if $SDP && /\brtpmap\b/; $SDP = 0; } close(LOG);
日志示例
Jan 28 11:39:37.525 CET: //1393628/D5CC0586A87B/SIP/Msg/ccsipDisplayMsg:^M Received:^M SIP/2.0 200 OK^M Via: SIP/2.0/UDP 10.218.16.2:5060;branch=z9hG4bKB22001ED5^M From: "Frankeerapparaat Secretariaat" <sip:089653717@10.210.2.49>;tag=E7E0EF64-192F^M To: <sip:022046187@10.210.2.49>;tag=25079324~19cc0abf-61d9-407f-a138-96eaffee1467-27521338^M Date: Mon, 28 Jan 2013 10:39:32 GMT^M Call-ID: D5CCA1AE-686D11E2-A881ED01-8DFA6D70@10.218.16.2^M CSeq: 102 INVITE^M Allow: INVITE, OPTIONS, INFO, BYE, CANCEL, ACK, PRACK, UPDATE, REFER, SUBSCRIBE, NOTIFY^M Allow-Events: presence^M Supported: replaces^M Supported: X-cisco-srtp-fallback^M Supported: Geolocation^M Session-Expires: 1800;refresher=uas^M Require: timer^M P-Preferred-Identity: <sip:022046187@10.210.2.49>^M Remote-Party-ID: <sip:022046187@10.210.2.49>;party=called;screen=no;privacy=off^M Contact: <sip:022046187@10.210.2.49:5060>^M Content-Type: application/sdp^M Content-Length: 209^M ^M v=0^M o=CiscoSystemsCCM-SIP 2000 1 IN IP4 10.210.2.49^M s=SIP Call^M c=IN IP4 10.210.2.1^M t=0 0^M m=audio 16844 RTP/AVP 8 101^M a=rtpmap:8 PCMA/8000^M a=ptime:20^M a=rtpmap:101 telephone-event/8000^M a=fmtp:101 0-15^M ^M Jan 28 11:39:37.529 CET: //1393628/D5CC0586A87B/SIP/Msg/ccsipDisplayMsg:^M Sent:^M ACK sip:022046187@10.210.2.49:5060 SIP/2.0^M Via: SIP/2.0/UDP 10.218.16.2:5060;branch=z9hG4bKB2247150A^M From: "Frankeerapparaat Secretariaat" <sip:089653717@10.210.2.49>;tag=E7E0EF64-192F^M To: <sip:022046187@10.210.2.49>;tag=25079324~19cc0abf-61d9-407f-a138-96eaffee1467-27521338^M Date: Mon, 28 Jan 2013 10:39:36 GMT^M Call-ID: D5CCA1AE-686D11E2-A881ED01-8DFA6D70@10.218.16.2^M Max-Forwards: 70^M CSeq: 102 ACK^M Authorization: Digest username="Genk_AC_1",realm="infraxnet.be",uri="sip:022046187@10.210.2.49:5060",response="9546733290a96d1470cfe29a7500c488",nonce="5V/Jt8FHd5I8uaoahshiaUud8O6UujJJ",algorithm=MD5^M Allow-Events: telephone-event^M Content-Length: 0^M ^M ^M Jan 28 11:39:37.529 CET: //1393627/D5CC0586A87B/SIP/Msg/ccsipDisplayMsg:^M Sent:^M SIP/2.0 200 OK^M Via: SIP/2.0/UDP 192.168.8.11:5060;branch=z9hG4bK24ecaaaa6dbd3^M From: "Frankeerapparaat Secretariaat" <sip:3717@192.168.8.11>;tag=e206cc93-1791-457a-aaac-1541296cf17c-29093746^M To: <sip:022046187@192.168.8.28>;tag=E7E0F8A4-EA3^M Date: Mon, 28 Jan 2013 10:39:32 GMT^M Call-ID: fedc8f80-10615564-45df0-b08a8c0@192.168.8.11^M CSeq: 101 INVITE^M Allow: INVITE, OPTIONS, BYE, CANCEL, ACK, PRACK, UPDATE, REFER, SUBSCRIBE, NOTIFY, INFO, REGISTER^M Allow-Events: telephone-event^M Remote-Party-ID: <sip:022046187@192.168.8.28>;party=called;screen=no;privacy=off^M Contact: <sip:022046187@192.168.8.28:5060>^M Supported: replaces^M Supported: sdp-anat^M Server: Cisco-SIPGateway/IOS-15.3.1.T^M Session-Expires: 1800;refresher=uas^M Require: timer^M Supported: timer^M Content-Type: application/sdp^M Content-Disposition: session;handling=required^M Content-Length: 247^M ^M v=0^M o=CiscoSystemsSIP-GW-UserAgent 7276 9141 IN IP4 192.168.8.28^M s=SIP Call^M c=IN IP4 192.168.8.28^M t=0 0^M m=audio 30134 RTP/AVP 8 101^M c=IN IP4 192.168.8.28^M a=rtpmap:8 PCMA/8000^M a=rtpmap:101 telephone-event/8000^M a=fmtp:101 0-15^M a=ptime:20^M ^M CLG(2022-11-07 00:09:06.444)| Call(Terminate) | 302A330B040C73070A021806021C0200 | ^M TRC(2022-11-15 00:00:38.012)| SIP( OUT : Response ) Trying( 100 INVITE ) | 2 | | 0 | 332C30050A0F750A00011A06021C0200 | SIP/2.0 100 Trying^M
问题分析
原代码存在几个关键问题:
- 读取日志文件后将内容存入
$line变量,但后续用while (<>)读取的是标准输入而非处理后的$line内容,逻辑完全错位。 - 段落模式的使用时机错误,未针对处理后的内容进行分割。
- 未实现
CLG与TRC行之间插入空行的需求。 - 检查Call-ID的逻辑有误,
and $ID仅判断变量非空,未匹配具体ID值。
修正后的代码
use strict; use warnings; my $target_id = "D5CCA1AE-686D11E2-A881ED01-8DFA6D70@10.218.16.2"; # 读取并处理文件内容 open my $log_fh, '<', 'file.log' or die "无法打开文件: $!"; local $/; # 一次性读取整个文件 my $content = <$log_fh>; close $log_fh; # 1. 移除所有CR字符(Windows格式的\r) $content =~ s/\r//g; # 2. 在CLG行和TRC行之间插入空行 $content =~ s/(CLG\(.*\).*)\n(TRC\(.*\).*)/$1\n\n$2/; # 3. 按段落模式处理内容 local $/ = ''; # 段落模式:空白行分隔的块 # 将处理后的内容转为段落流 open my $processed_fh, '<', \$content or die "无法打开内存流: $!"; while (my $paragraph = <$processed_fh>) { # 检查段落是否包含目标Call-ID if ($paragraph =~ /Call-ID:\s+$target_id/) { print "=== 匹配到目标Call-ID的段落 ===\n"; print $paragraph; # 提取并打印段落中的rtpmap行 print "\n=== 段落中的rtpmap行 ===\n"; print "$_\n" for ($paragraph =~ /^a=rtpmap:.+/mg); print "\n"; } } close $processed_fh;
代码说明
- 移除CR字符:用正则
s/\r//g全局替换所有\r,将Windows格式转为Unix格式换行。 - 插入空行:用正则匹配
CLG开头的行和紧跟的TRC行,在中间插入一个空行。 - 段落模式处理:通过内存文件句柄将处理后的内容按空白行分割为段落,逐个检查每个段落是否包含目标Call-ID:
- 若匹配则打印整个段落。
- 同时提取段落中所有
a=rtpmap开头的行单独打印。
- 改用词法文件句柄
$log_fh替代旧式的LOG,更符合Perl现代编程规范。
内容的提问来源于stack exchange,提问作者shambhala
相关产品推荐
相关产品推荐

