Perl脚本无\n时print输出为空及哈希填充失败问题咨询
Perl脚本print输出异常与哈希表为空问题解析
问题背景
作为Perl新手,我编写了一段脚本从配置文件提取信息填充哈希表,但遇到两个异常:
- 变量
$service作为哈希键,直接执行print $service无输出,添加\n后才显示内容; - 不加
\n时哈希表为空。
配置文件片段
edit "DNS" set category "Network Services" set tcp-portrange 53 set udp-portrange 53 next edit "HTTP" set category "Web Access" set tcp-portrange 80 next ...(其余配置片段略)
原脚本片段
#!/usr/local/bin/perl use strict; use warnings; my $InFile = "/mnt/c/Users/claidin/OneDrive - Hitachi Vantara/Desktop/Perso/Firewalling/log-rules/import-svc_objet.conf"; open (InFile,"$InFile") or die "Can't open file\n"; my $Ligne; my %svcDB =(); my $svcname; my $service; while ($Ligne=<InFile>) { if ($Ligne=~ /set\s(.*)\-portrange\s(.*)\n/) { $service = (uc($1)."_".$2); print $service; # print "$service\n"; } }
加\n时的输出示例
claidin@PF49BCDO:/mnt/c/Users/claidin/OneDrive-HitachiVantara/Desktop/Perso/Firewalling/log-rules$ perl extract-svc-test1.pl TCP_53 UDP_53 TCP_80 ...(其余输出片段略)
问题原因解析
1. print $service无输出的原因
Perl的标准输出(STDOUT)默认采用行缓冲机制:只有当输出内容包含换行符\n、缓冲区被手动刷新,或者程序终止时,缓冲区的内容才会被输出到终端。直接执行print $service时,内容暂存于缓冲区未被刷新,因此终端无显示;添加\n后触发缓冲区刷新,内容才会显示。
2. 哈希表为空的原因
你提供的脚本片段完全没有往哈希表%svcDB写入数据的代码,无论是否添加\n,哈希表都会是空的。此外,原正则表达式存在缺陷,可能导致$service的值不符合预期,间接影响哈希表的填充:
- 正则
/set\s(.*)\-portrange\s(.*)\n/使用贪婪匹配(.*),可能捕获到多余空白字符或无效内容; - 强制匹配行尾
\n,若文件最后一行无换行符则匹配失败; - 未忽略行首的缩进空白,导致部分行无法匹配。
改进方案
1. 修复正则表达式
使用非贪婪匹配和行锚定,确保准确提取端口类型和端口号:
/^\s*set\s(.*?)\-portrange\s(\d+)\s*$/
^\s*:匹配行首任意空白字符(兼容配置文件的缩进);(.*?):非贪婪匹配端口类型(tcp/udp);(\d+):仅匹配数字类型的端口号;\s*$:匹配行尾任意空白(兼容无换行符的行)。
2. 添加哈希表写入逻辑
在生成$service后,添加代码将其存入哈希表,例如:
$svcDB{$service} = $2; # 将端口号作为值存入哈希表,可根据需求调整
3. 手动刷新缓冲区(可选)
若不想依赖\n触发输出,可手动刷新缓冲区:
use IO::Handle; # 先导入模块 STDOUT->flush(); # 在print后调用
4. 使用现代Perl文件句柄
替换旧式全局文件句柄为词法文件句柄,提升代码安全性:
open my $fh, '<', $InFile or die "Can't open file: $!";
修改后的完整脚本
#!/usr/local/bin/perl use strict; use warnings; use IO::Handle; # 用于手动刷新缓冲区 my $InFile = "/mnt/c/Users/claidin/OneDrive - Hitachi Vantara/Desktop/Perso/Firewalling/log-rules/import-svc_objet.conf"; open my $fh, '<', $InFile or die "Can't open file: $!"; my %svcDB = (); my $service; while (my $Ligne = <$fh>) { if ($Ligne =~ /^\s*set\s(.*?)\-portrange\s(\d+)\s*$/) { $service = uc($1) . "_" . $2; print $service; STDOUT->flush(); # 手动刷新缓冲区,无需\n也能输出 $svcDB{$service} = $2; # 将端口号作为值存入哈希表 } } close $fh; # 验证哈希表内容 print "\n\n哈希表内容:\n"; foreach my $key (sort keys %svcDB) { print "$key -> 端口号:$svcDB{$key}\n"; }
内容的提问来源于stack exchange,提问作者Metharil
相关产品推荐
相关产品推荐

