perlprintingchomp

Perl weird chomp and newline


I'm having weird things when extracting line from file.

Lines are coming from SSH commands sent on a router and saved into a file. They are looking like : 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

So, in my PERL script, when i'm reading this file, I make this :

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);

The output should be :

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

But unfortunately, the output is more like :

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

I guess there's a newline somewhere, probably at the end of each interface line. But i tried several things, like chomp($_), or even

$_ =~ s/
//;

But things get weirder. EDIT : I tried other answers, having same issue. Using Borodin's answer :

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);

The output is like

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

We are getting closer to it, what happened to the others FastEthernet interfaces ?


Solution

  • The problem is that you are processing a file from a Windows system that has CRLF at the end of each line instead of just LF (newline). chomp removes the newline, but the carriage-return remains and messes up your output. You can get around this by using s/\s+\z// instead of chomp.

    But you can avoid the problem with line terminators altogether by using split with a single space string (not a regex). That is the default if you don't pass any parameters at all, and it splits on any combination of whitespace (which includes newlines and carriage-returns), ignoring any leading spaces.

    This program seems to do what you want. It makes use of regular expressions instead of split to get straight to the correct part of the data without using temporary arrays.

    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
    

    output

    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