Perl weird chomp and newline
Question
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
OTHER TIPS
I am not getting anything weird in your code but you are not using chomp anywhere in the code.
Just add chomp $_;
at the very start of your while loop.
Try this Code:
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
and this is the output:
FastEthernet0 ; upup ; not set
FastEthernet1 ; upup ; not set
FastEthernet2 ; updown ; not set
Try this $etat =~ s/[\n]+$//; or if that fails $etat =~ s/[\r\n]+$//;
I suspect you have one of these as the end of your newlines in the file being read in. I also suspect a single chomp may not work if the line ends in \r\n.
Without knowing the actual input file exact details the below is a suggestion and is written based on the contents of the input file you have provided in your post.
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);