You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Perl脚本无\n时print输出为空及哈希填充失败问题咨询

Perl脚本print输出异常与哈希表为空问题解析

问题背景

作为Perl新手,我编写了一段脚本从配置文件提取信息填充哈希表,但遇到两个异常:

  1. 变量$service作为哈希键,直接执行print $service无输出,添加\n后才显示内容;
  2. 不加\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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 10:57:37