Perl Script Help | Readthrough group of files, looking for specific match - perl

Here is what im trying to achieve.
I have a perl script that looks into a group of directly parse the files it finds and locates a specific string. If it finds the specific string, it ignores it, if it finds the first part of the string, but the second part doesnt match, it writes it out to a log file.
The part im stuck with, is how to read the entire file in, and look to see if the string exists in the entire file.
Some background, im trying to write a script that will read cisco rancid files, and looks for the sys logging details. These are stored in the config as such
logging x.x.x.x
where x.x.x.x is the syslog IP address.
Currently, i can read in the file, check it line by line, and see if the file contains logging x.x.x.x, if it does, it ignores it.
If it contains logging y.y.y.y (where y is an IP different to what it should be), it will write out to a log file, that the logging is setup, but incorrectly configured. But i cant for the life of me work out how to get it to read the entire file, and if logging x.x.x.x or even logging y.y.y.y doesnt exist to write out that it is not configured.
script is below
#!/usr/bin/perl
use strict;
use warnings;
#Syslog Checker.... V0.0.1
##Config Items
my $basedir = "/home/srelf/Documents/Projects/perl/Configs";
my #verdir = qw(sw_a);
my $fulldir;
my $configs;
my $loghost = "x.x.x.x";
my $combidir;
use POSIX qw(strftime);
$now_string = strftime "%a%b%e%Y", gmtime;
open FILE, ">>Configchecker.$now_string.txt" or die $!;
print FILE "### Logging Host Settings ###\n";
close FILE;
foreach $combidir (#verdir) {
$fulldir = "$basedir/$combidir";
opendir( DIR, $fulldir );
my #files = grep { $_ ne '.' && $_ ne '..' } readdir DIR;
closedir(DIR);
while ( #files > 0 ) {
$configs = pop #files;
# print "$fulldir/$configs\n"; # used for debug shows current file with full path.
open FH, "$fulldir/$configs" or die $!;
#dataarry = <FH>;
foreach my $line (#dataarry) {
# Start an if statement, the condition of which is
# "If this particular line contains logging + IP address."
if ( $line =~ m/logging \b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b/i ) {
#then if the line located above contains logging and the log host ignore it
if ( $line =~ m/logging $loghost/i ) {
}
# if the above line contains an ip but it is not the correct ip do the below.
elsif ( $line =~
m/logging \b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b/i )
{
open FILE, ">>Configchecker.$now_string.txt" or die $!;
print FILE "$configs\n";
print FILE
"Logging for this device is set, but pointing at the wrong host: $line\n";
close FILE;
} # End the if condition here.
}
}
} # End the foreach loop here;
open FILE, ">>Configchecker.$now_string.txt" or die $!;
print FILE "### NTP Settings Check ###\n";
close FILE;
}
Thanks in advance for any help.
Im very new to perl, this is my first shot.
Steve.

foreach my $configs (#files)
{
my $CONFIGURED=0;
open FH, "$fulldir/$configs" or die $!;
while (<FH>)
{
if ($_ =~ m/logging/)
{
$CONFIGURED++;
}
if ($_ =~ m/logging \b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b/i and $_ !~ m/logging $loghost/i)
{
print "Logging for this device is set, but pointing at the wrong host: $_\n";
}
}
if ($CONFIGURED == 0)
{
print "NOT CONFIGURED $configs\n";
}
}

Related

How do I find the line a word is on when the user enters text in Perl?

I have a simple text file that includes all 50 states. I want the user to enter a word and have the program return the line the specific state is on in the file or otherwise display a "word not found" message. I do not know how to use find. Can someone assist with this? This is what I have so far.
#!/bin/perl -w
open(FILENAME,"<WordList.txt"); #opens WordList.txt
my(#list) = <FILENAME>; #read file into list
my($state); #create private "state" variable
print "Enter a US state to search for: \n"; #Print statement
$line = <STDIN>; #use of STDIN to read input from user
close (FILENAME);
An alternative solution that reads only the parts of the file until a result is found, or the file is exhausted:
use strict;
use warnings;
print "Enter a US state to search for: \n";
my $line = <STDIN>;
chomp($line);
# open file with 3 argument open (safer)
open my $fh, '<', 'WordList.txt'
or die "Unable to open 'WordList.txt' for reading: $!";
# read the file until result is found or the file is exhausted
my $found = 0;
while ( my $row = <$fh> ) {
chomp($row);
next unless $row eq $line;
# $. is a special variable representing the line number
# of the currently(most recently) accessed filehandle
print "Found '$line' on line# $.\n";
$found = 1; # indicate that you found a result
last; # stop searching
}
close($fh);
unless ( $found ) {
print "'$line' was not found\n";
}
General notes:
always use strict; and use warnings; they will save you from a wide range of bugs
3 argument open is generally preferred, as well as the or die ... statement. If you are unable to open the file, reading from the filehandle will fail
$. documentation can be found in perldoc perlvar
Tool for the job is grep.
chomp ( $line ); #remove linefeeds
print "$line is in list\n" if grep { m/^\Q$line\E$/g } #list;
You could also transform your #list into a hash, and test that, using map:
my %cities = map { $_ => 1 } #list;
if ( $cities{$line} ) { print "$line is in list\n";}
Note - the above, because of the presence of ^ and $ is an exact match (and case sensitive). You can easily adjust it to support fuzzier scenarios.

Perl to find the extension of file

I have a program that takes directory name as input from user and searches all files inside the directory and prints the contents of file. Is there any way so that I can read the extension of file and read the contents of file that are of specified extension? For example, it should read contents of file that is in ".txt" format.
My code is
use strict;
use warnings;
use File::Basename;
#usr/bin/perl
print "enter a directory name\n";
my $dir = <>;
print "you have entered $dir \n";
chomp($dir);
opendir DIR, $dir or die "cannot open directory $!";
while ( my $file = readdir(DIR) ) {
next if ( $file =~ m/^\./ );
my $filepath = "${dir}${file}";
print "$filepath\n";
print " $file \n";
open( my $fh, '<', $filepath ) or die "unable to open the $file $!";
while ( my $row = <$fh> ) {
chomp $row;
print "$row\n";
}
}
To get just the ".txt" files, you can use a file test operator (-f : regular file) and a regex.
my #files = grep { -f && /\.txt$/ } readdir $dir;
Otherwise, you can look for just text files, using perl's -T (ascii-text file test operator)
my #files = grep { -T } readdir $dir;
Otherwise you can try even this:
my #files = grep {-f} glob("$dir/*.txt");
You're pretty close here. You have a main loop that looks like this:
while ( my $file = readdir(DIR) ) {
next if $file =~ /^\./; # skip hidden files
# do stuff
}
See where you're skipping loop iterations if the filename starts with a dot. That's an excellent place to put any other skip requirements that you have - like skipping files that don't end with '.txt'.
while ( my $file = readdir(DIR) ) {
next if $file =~ /^\./; # skip hidden files
next unless $file =~ /\.txt$/i; # skip non-text files
# do stuff
}
In the same way as your original test checked for the start of the string (^) followed by a literal dot (\.), we're now searching for a dot (\.) followed by txt and the end of the string ($). Note that I've also added the /i option to the match operator to make the match case-insensitive - so that we match ".TXT" as well as ".txt".
It's worth noting that the extension of a file is a terrible way to work out what the file contains.
Try this. Below code gives what you expect.
use warnings;
use strict;
print "Enter the directory name: ";
chomp(my $dir=<>);
print "Enter the file extension type: "; #only type the file format. like txt rtf
chomp(my $ext=<>);
opendir('dir',"$dir");
my #files = grep{m/.$ext/g} readdir('dir');
foreach my $ech(#files){
open('file',"$dir/$ech");
print <file>;
}
I'm store the all file from the particular directory to store the one array and i get the particular file format by using the grep command. Then open the files into the foreach condition

Perl - Read multiple files and read line by line of the text file

I am trying to read multiple .txt files in a folder. Each file should be read line by line, however, I failed to read multiple .txt files by using glob. Any advice on my code?
my %data;
#FILES = glob("*.txt");
$EmailMsg .= "EG. Folder(week) = Folder(CW01) --CW01 = Week 1 -- Number is week\n ";
$EmailMsg .= "=======================================================================================================\n";
# Try to Loop multiple files here
foreach my $file (#FILES) {
local $/ = undef;
open my $fh, '<', $file;
$data{$file} = <$fh>;
# Read the file one line at a time.
while (my $line = <$fh>) {
chomp $line;
$line =~ s/^\s+//;
$line =~ s/\s+$//;
my ($name, $date, $week) = split /\:/, $line;
if ($name eq "NoneFolder") {
$EmailMsg .= "Folder ($week) - No Folder created on the FTP! Failed to open folder!\n";
}
if ($name eq "EmptyFiles") {
$EmailMsg .= "Folder ($week) - No Files insides the folder! Failed download files!\n";
}
}
}
$EmailMsg .= "=======================================================================================================\n";
$EmailMsg .= "Please note that if you receive this email means that the script is running fine just that no folder is created or no files inside the folder for the week on the FTP.\n";
# close the file.
#close <$fh>;
Currently output:
EG. Folder(week) = Folder(CW01) --CW01 = Week 1 -- Number is week
=======================================================================================================
=======================================================================================================
Please note that if you receive this email means that the script is running fine just that no folder is created or no files inside the folder for the week on the FTP.
It failed to get any .txt files.
You are trying to read each file twice: firstly into the hash %data and then again line by line.
Once you have reached end of file, you have to either reopen the file or use seek to move the read pointer back to the beginning.
You also need to set $/ back to its original value, otherwise your loop will read the entire file instead of one line at a time.
It's not clear whether you really need the second copy of the file data in the hash, but you can avoid having to reset $/ by putting the change within a block, like this
open my $fh, '<', $file;
$data{$file} = do {
local $/ = undef;
<$fh>;
};
and then reset the file pointer to the start again before the while loop.
seek $fh, 0, 0;
#!/usr/bin/perl
use strict;
use warnings FATAL => 'all';
my #files=('Read a file.pl','Read a single text file.pl','Read only one
file.pl','Read the file using while.pl','Reading the file.pl');
foreach my $i(#files) {
open(FH, "<$i");
{
while (my $row = <FH>) {
chomp $row;
print "$row\n";
}
}
}
The file globbing works for me. You might want to specify scope for your #FILES variable and check that there actually are files matching the path you have specified,
#!/bin/env perl
use strict;
use warnings;
## glob on all files in home directory
## see: http://perldoc.perl.org/File/Glob.html
use File::Glob ':globally';
my #configs = <~myname/project/etc/*.cfg>;
foreach my $fn (#configs) {
print "file $fn\n";
}
your code,
my %data;
#here are some .c files,
my #FILES = glob("../*.c");
foreach my $fn (#FILES) {
print "file $fn\n";
}
exit;
This way catches more garbage for about the same amount of code.
my $PATH = shift #ARGV ;
chomp $PATH ;
opendir(TXTFILE,$PATH) || die ("failed to opendir: $PATH") ;
my #file = readdir TXTFILE ;
closedir(TXTFILE) ;
foreach(#file) { #
next unless ($_ =~ /\.txt$/i) ; # Only get .txt files
$PATH =~ s/\/$//g ; $PATH =~ s/$/\// ; # Uniform trailing slash
my $thisfile = $PATH . $_ ; # now a fully qualified filename
unless (open(THISFILE,$thisfile)) { # Notify on busted files.
warn ("$thisfile failed to open") ;
next ;
}
while(<THISFILE>) {
# etc. etc.
}
close(THISFILE) ;
}

Reading and printing file contents using Perl

Can any body help me in reading all the files of particular format from the directory line by line and it should print on screen.
And my request is to include command lines in the program itself.
Then when ever simple I ran the program , it should display all the content of files.
Below is the program I wrote can any body help me please....
#!/usr/local/bin/perl
$filepath="/home/hclabv";
opendir(DIR,"$filepath");
#files=grep{/\.out$/} readdir(DIR);
closedir(DIR);
$c = 0;
for ($c=0 ;
while ($c <= #files)
{
$cmd = "Perlsc11 $files[$c]";
system($cmd);
if($#ARGV != 0) {
print STDERR "You must specify exactly one argument.\n";
exit 4;
}
else
{
print ("$files[$c]\n");
# Open the file.
open(INFILE, $ARGV[0]) or die "Cannot open $ARGV[0]: $!.\n";
while(my $l = <INFILE>) {
print $l;
}
close INFILE;
}
$c++;
}
You can use the glob feature in perl to get a list of filenames with the ".out" extension in the specified directory. You can then open these files one by one using a loop and print their contents to the screen. Here's the code,
# get all file-names with ".out" extension into array
my #outFiles = glob "/home/hclabv/*.out";
# loop through list of file names in array
foreach my $outFileName ( #outFiles )
{
# open the file for processing
open $outFile, '<', $outFileName or die "Unable to open file for reading : $!";
# iterate through each line in the file
while ( $line = <$outFile> )
{
# print the individual line
print "$line\n";
}
# close the file
close $outFile;
}
Please clarify what you mean by "including command lines", so we can help further.

Perl loop through text files in a directory, locate file based on file name and print content

How do i loop through files in a directory, locate file based on file name and print file content?
Please see below code:
files in directory:
1234.txt
345.txt
234.txt
Code:
opendir (DIR, "LOCATION")|| die "cant open directory\n";
my #DATA = grep {(!/^\./)} readdir (DIR);
while ( my $file = shift #DATA) {
open FILE, "LOCATION";
while (FILE){
if ($file eq "235") {
print $_;
}
}
}
This should do (untested):
opendir( DIR, "/path/to/dir" );
while ( my $entry = readdir( DIR ) ) {
if ( $entry =~ /^$filenameImLookingFor$/ ) {
open( FILE, "$entry/$filenameImLookingFor" );
my #lines = <FILE>;
close( FILE );
print( join( '', #lines );
}
}
closedir( DIR );
The code in your question:
opendir (DIR, "LOCATION")|| die "cant open directory\n";
my #DATA = grep {(!/^\./)} readdir (DIR);
while ( my $file = shift #DATA) {
open FILE, "LOCATION";
while (FILE){
if ($file eq "235") {
print $_;
}
}
}
Will do this:
First it will handily find all files in directory "LOCATION" that do not begin with a period. Then it will iterate in a rather odd loop over each file name. The normal version of this loop would be:
for my $file (#DATA)
Then it will attempt to open the directory "LOCATION" again. This will likely fail, because "LOCATION" is a directory. Since you do not check the return value with die, this error will be silent.
What you probably want is to use
if ($file eq "235.txt") {
open my $fh, "<", $file or die $!;
print <$fh>;
}
This part:
while (FILE)
Is not actually checking the return value of readline(), it is checking whether the file handle is returning a true value. As near as I can tell on my system, it does return a true value even if the open failed. Which means of course that the loop will run indefinitely. What you probably meant was
while (<FILE>)
However, as explained earlier, this will only result in the error "readline() on unopened file handle FILE" since the open statement cannot open a directory.
Your check
if ($file eq "235")
Will never be true, since you said your file names had a .txt extension. You might instead do
if ($file eq "235.txt")
Which should work.
If you wanted to be clever, you could include your check directly in the grep:
my #files = grep { $_ eq "235.txt" } readdir DIR;
And since perl can use the <> diamond operator to print files listed in the #ARGV array, you can even do this
#ARGV = grep { $_ eq "235.txt" } #ARGV;
print <>;
Assuming you call the script with:
perl script.pl dir/*.txt
This is, of course, just the long version of doing:
perl -pe0 235.txt
Which is the long version of
cat 235.txt
So, I get the feeling you are trying to do something other than what your code implies.