【问题标题】:Perl weird chomp and newlinePerl 奇怪的 chomp 和换行符
【发布时间】:2014-07-31 09:08:13
【问题描述】:

从文件中提取行时我遇到了奇怪的事情。

行来自在路由器上发送并保存到文件中的 SSH 命令。 他们看起来像: saved_commands

    FastEthernet0 is up, line protocol is up
    Helper address is not set
    FastEthernet1 is up, line protocol is up
    Helper address is not set
    FastEthernet2 is up, line protocol is down
    Helper address is not set

所以,在我的 PERL 脚本中,当我阅读这个文件时,我会这样做:

while (<LIRE>) {
    $ifname = "";
    $etat = "";
    $myip = "";
    if (/line protocol/) {
        @status = split(/,/, $_) ;
        @interface = split(/ /, $_);
        $ifname = $interface[0];
        for ($index = 0; $index <= $#status; $index++) {
            @etat_par_if = split(/ /, $status[$index]);
            $etat .= pop(@etat_par_if);
        }
    }
    if (/Helper|helper/) {
        @helper = split(/ /, $_) ;
        $myip = pop(@helper);
        if ($myip =~ /126/) {

        }
        else {
            $myip = "not set";
        }
    }
    if ($myip eq ""){
        $myip = "not set";
    }       
    if ($ifname ne ""){
    print "$ifname ; $etat ; $myip \n";
    }
}
close(LIRE);

输出应该是:

FastEthernet0 ; upup ; not set
FastEthernet1 ; upup ; not set
FastEthernet2 ; updown ; not set

但不幸的是,输出更像:

FastEthernet0 ; upup
 ; not set
FastEthernet1 ; upup
 ; not set
FastEthernet2 ; updown
 ; not set

我猜某处有一个换行符,可能在每个界面行的末尾。 但是我尝试了几件事,比如chomp($_),甚至是

$_ =~ s/
//;

但是事情变得更奇怪了。 编辑 : 我尝试了其他答案,遇到了同样的问题。 使用鲍罗丁的回答:

my ($etat, $ifname, $myip);

open(DATA,$fichier) || die ("Erreur d'ouverture de $fichier\n") ;
while (<DATA>) {

    if (/line protocol/) {
        $ifname = (split)[0];
        my @status    = split /,/;
        for (@status) {
          $etat .= (split)[-1];
        }
    }
    elsif (/helper/i) {
        my @helper = split;
        $myip = (split)[-1];
        $myip = 'not set' unless $myip =~ /\d/;
    }

    if ($ifname and $myip) {
      print "$ifname ; $etat ; $myip\n";
      $etat = $ifname = $myip = undef;
    }

}
close(DATA);

输出是这样的

FastEthernet0 ; upup ; not set
Vlan1 ; downdowndowndowndowndownupupupdownupdownupdownupdownupdownupdownupdownupup ; 126.0.24.130
Loopback0 ; upup ; not set
Loopback1 ; upup ; not set
Tunnel100 ; upupupupupup ; not set
Dialer1 ; upup ; not set
Tunnel101 ; upup ; not set
Tunnel200 ; upup ; not set
Tunnel201 ; upup ; not set
Tunnel300 ; upup ; not set

我们越来越接近它,其他 FastEthernet 接口发生了什么?

【问题讨论】:

  • 您在代码中的哪个位置执行chomp($_)
  • 我删除了它。我将进行编辑以向您展示 chomp 会发生什么
  • chomp 实际上做了什么
  • 添加了 chomp 代码更改。好像界面消失了或者发生了一些奇怪的事情
  • use Data::Dumper; $Data::Dumper::Useqq = 1; print Dumper($_); print Dumper($/);。这将向您显示行尾的内容以及 chomp 将尝试删除的内容。

标签: perl printing chomp


【解决方案1】:

问题是您正在处理来自 Windows 系统的文件,该文件在每行末尾都有 CRLF,而不仅仅是 LF(换行符)。 chomp 删除了换行符,但回车符仍然存在并且会打乱你的输出。您可以使用s/\s+\z// 而不是chomp 来解决此问题。

但是你可以通过使用带有单个空格字符串(不是正则表达式)的split 来完全避免行终止符的问题。如果您根本不传递任何参数,则这是默认设置,它会根据空格的任何组合(包括换行符和回车符)进行拆分,忽略任何前导空格。

这个程序似乎做你想做的事。它使用正则表达式而不是 split 来直接获取数据的正确部分,而无需使用临时数组。

use strict;
use warnings;

my $fichier = 'fichier.txt';

open my $lire, '<', $fichier or die "Erreur d'ouverture de $fichier: $!";

my ($ifname, $etat, $myip);

while (<$lire>) {

   if (/line protocol/) {

      if ($ifname) {
         printf "%s ; %s ; %s\n", $ifname, $etat, $myip // 'not set';
         $myip = undef;
      }

      ($ifname) = /(\S+)/;
      $etat = join '', /(\S+)\s*(?:,|\z)/g;
   }
   elsif (/helper.*\s([\d.]+)/i) {
      $myip = $1;
   }

}

if ($ifname) {
   printf "%s ; %s ; %s\n", $ifname, $etat, $myip // 'not set';
}

fichier.txt

FastEthernet0 is up, line protocol is up
Helper address is not set
FastEthernet1 is up, line protocol is up
Helper address is not set
FastEthernet2 is up, line protocol is down
Helper address is 126.0.24.130
FastEthernet3 is up, line protocol is down
FastEthernet4 is up, line protocol is down
Helper address is 126.0.24.128
FastEthernet5 is up, line protocol is down

输出

FastEthernet0 ; upup ; not set
FastEthernet1 ; upup ; not set
FastEthernet2 ; updown ; 126.0.24.130
FastEthernet3 ; updown ; not set
FastEthernet4 ; updown ; 126.0.24.128
FastEthernet5 ; updown ; not set

【讨论】:

  • 就是这样。按我的意愿工作。谢谢鲍罗丁和大家。
  • 我想我已经意识到你真正需要什么了。应始终显示“线路协议” 记录。如果它有对应的 "helper" 记录和有效的 IP 地址,则第三个输出字段显示该地址,否则显示not set
【解决方案2】:

我没有在你的代码中发现任何奇怪的东西,但你没有在代码中的任何地方使用 chomp。

只需在 while 循环的最开始添加 chomp $_;

试试这个代码:

while (<DATA>) {
    chomp;
    $ifname = "";
    $etat = "";
    $myip = "";
    if (/line protocol/) {
        @status = split(/,/, $_) ;
        @interface = split(/ /, $_);
        $ifname = $interface[0];
        for ($index = 0; $index <= $#status; $index++) {
            @etat_par_if = split(/ /, $status[$index]);
            #chomp @etat_par_if;
            $etat .= pop(@etat_par_if);
        }
    }
    if (/Helper|helper/) {
        @helper = split(/ /, $_) ;
        $myip = pop(@helper);
        if ($myip =~ /126/) {

        }
        else {
            $myip = "not set";
        }
    }
    if ($myip eq ""){
        $myip = "not set";
    }       
    if ($ifname ne ""){
    print "$ifname ; $etat ; $myip \n";
    }
}
close(DATA);


__DATA__
FastEthernet0 is up, line protocol is up
Helper address is not set
FastEthernet1 is up, line protocol is up
Helper address is not set
FastEthernet2 is up, line protocol is down
Helper address is not set

这是输出:

FastEthernet0 ; upup ; not set 
FastEthernet1 ; upup ; not set 
FastEthernet2 ; updown ; not set 

【讨论】:

  • 我在之前的cmets中做过。输出没有任何反应。在 helper 和 etat 之间有这个奇怪的接口名称
  • 用代码和输出编辑了我的答案...检查一下
  • 这太棒了,因为我没有和你一样的输出。仍然有我的错误的东西......我不能写给你我描述我的接口的所有文件(互联网地址是......广播地址是...... MTU是......)所以你告诉我这是我的输入文件问题?
  • 您能否提供完整线路的示例,也许只需将任何真实 IP 地址更改为 172.10.0.0 之类的其他地址。如果人们了解全貌,就更容易提供帮助。
【解决方案3】:

试试这个 $etat =~ s/[\n]+$//;或者如果失败 $etat =~ s/[\r\n]+$//;

我怀疑您在正在读入的文件中将其中一个作为换行符的结尾。我还怀疑如果该行以 \r\n 结尾,则单个 chomp 可能不起作用。

【讨论】:

    【解决方案4】:

    在不知道实际输入文件的确切细节的情况下,以下是一个建议,是根据您在帖子中提供的输入文件的内容编写的。

    use strict;
    use warnings;
    
    open (my $file, '<', 'LIRE.txt') or die "unable to open file : $!\n";
    while (<$file>) {
            chomp();
            if (/line protocol/){
                    my ($ifname) = /^([A-Za-z0-9]+)\s/;
                    my @status = map { /\s([A-Za-z]+)$/ } split(',');
                    print "$ifname ; ", @status;
            } elsif (/[Hh]elper/){
                    my $ip = /\s(\w+)$/;
                    print " ; ", $ip =~ /126/ ? $ip : "not set", "\n";
            }
    }
    close($file);
    

    【讨论】:

    • /[Hh]elper/ 最好写成/helper/i。而且您无法知道 OP 的界面名称中有哪些字符,并且您不能只假设字母数字。只需my ($ifname) = /(\S+)/ 就可以了。 split 的第一个参数是一个正则表达式,你应该这样标记它,就像这样split /,/
    • 你无法知道IP地址是否在行尾结束。它当然不只包含\w 字符。此外,my $ip = /\s(\w+)$/ 会将$ip 设置为 truefalse 而不是 IP 地址本身。你需要像my ($ip) = /(\S+)\s*$/ 这样的东西。如果你这样写,也不需要chomp
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多