Perl find whether more than one string is in a log file then print - perl

Hi just learning Perl so please forgive. What I am trying to do is if the log file contains one or more of these strings Failed, Error or Skipped then print out an error.(the log could have one or more of all of the strings in the arrary or just one instance) I have tried variations on the below code. It compiles but it only prints out Done.
#!perl=C:\IBM\RationalSDLC\ClearCase\bin\perl
my #matches = (
qr/"Failed"/,
qr/"Skipped"/,
qr/"Error"/,
);
$mylog = "email_log.txt";
open (LOG, "<$mylog") || die 'Could not OPEN $mylog file';
LOG_READER:
while (my $loglines = <$LOG>) {
if ($loglines =~ /#matches/) {
print "Error in the log\n";
last LOG_READER;
}
}
close($LOG);
print "\n Test is Done \n";
I have also tried
while ( $_ = <INPUT> ) {
if( $_ =~ #matches ){
print "\n Build Failed! \n";
}
}
thank you for any assistance
Modified code that works
This solution uses an array
#!perl=C:\IBM\RationalSDLC\ClearCase\bin\perl
my #matches = qr/Failed | Skipped | Error/x;
my $mylog = "email_log.txt";
open (LOG, "<$mylog") || die 'Could not OPEN $mylog file';
LOG_READER:
while (my $loglines = <LOG>) {
if ($loglines =~ /$matches/) {
print "Error in the log\n";
last LOG_READER;
}
}
close(LOG);
print "\n Test is Done \n";
This solution is what I used
#!perl=C:\IBM\RationalSDLC\ClearCase\bin\perl
use warnings;
use strict;
my $myfailures = qr/Failed | Skipped | Error/x;
my $mylog = "email_log.txt";
open (LOG, "<$mylog") || die 'Could not OPEN $mylog file';
LOG_READER:
while (my $loglines = <LOG>) {
if ($loglines =~ /$myfailures/) {
print "Error in the log\n";
last LOG_READER;
# last;
}
}
close(LOG);
print "\n Test is Done \n";
Another solution
#!perl=C:\IBM\RationalSDLC\ClearCase\bin\perl
use warnings;
use strict;
my $mylog = "email_log.txt";
open (INPUT, "<$mylog") || die 'Could not OPEN $mylog file';
LOG_READER:
while(<INPUT>) {
next unless /(Failed|Skipped|Error)/;
print "\n Build failed \n";
last;
}
close(INPUT);
print "\n Test is Done \n";
Solution #4
#!perl=C:\IBM\RationalSDLC\ClearCase\bin\perl
use warnings;
use strict;
my $matches = "Failed|Skipped|Error";
my $mylog = "email_log.txt";
open my $LOG, "<", $mylog|| die 'Could not OPEN $mylog file';
while (my $loglines = <$LOG>) {
if ($loglines =~ /$matches/) {
print "\n Error in the log\n";
last;
}
}
close($LOG);
print "\n Test is Done \n";
Thank you everyone for the contributions. They all worked really wish I could give everyone the correct answer. With the provided solutions I was able to get all the variations working.

if ($loglines =~ /#matches/) {
This is your problem. You cannot use an array as the regex to match against. Try this instead:
if ($loglines =~ /"(Failed|Skipped|Error)"/) {
while (my $loglines = <$LOG>) {
...
close($LOG);
Oh and this should be just LOG without the dollar signs.
With use strict; use warnings; you would get some better diagnostics. Get into the habit.

You are making this far too complicated. You really just need to do some reading on how regexps work, but its quite a simple problem to solve:
while(<INPUT>) {
next unless /"(Failed|Skipped|Error)"/;
print "\n Build failed \n";
last;
}

You could use array of regex but for this particular case it's not needed,
my $matches = qr/"Failed" | "Skipped" | "Error"/x;
and then
if ($loglines =~ /$matches/) { .. }

First of all. I would always use use warnings; use strict;.
I would also modify your open calls to use the 3-arg, lexical scoped version.
open my $fh, "<", $mylog;
What exactly are you trying to match here?
my #matches = (
qr/"Failed"/,
qr/"Skipped"/,
qr/"Error"/,
);
An example from your log file would help. You're trying to match these keywords wrapped in actual quotes. Is this what you intended?
email_log.txt example:
Your build "Failed"
"Skipping" foo.c
This will match with or without surrounding quotes:
my $matches = "Failed|Skipped|Error";
my $mylog = "email_log.txt";
open my $fh, "<", $mylog;
while (my $loglines = <$fh>) {
if ($loglines =~ /$matches/) {
print "Error in the log\n";
last;
}
}
close($fh);

Related

How to print part of header and string in another file in Perl

I am starting to work in Perl. I have a file called file_IN.txt which contains the following lines:
SITE 1
/def="CODE:X22"
/pattern="BBAACCLLDDCC"
SITE 2
/def="CODE:X33".
/pattern="MMKKNNJJXXVV"
I want to print all "patterns" (i.e. BBAACCLLDDCC) in another file called file_OUT.txt, taking into consideration the "def" part (i.e. X22) as a header:
Example outputs:
>X22
BBAACCLLDDCC.
>X33
MMKKNNJJXXVV
I tried the following code:
#!/usr/bin/perl -w
use warnings;
if(!open(MY_HANDLE, "file_IN.txt")){
die "Cannot open the file";
}
#content = <MY_HANDLE>;
close(MY_HANDLE);
if(!open(WRITE_HANDLE, ">>file_OUT.txt")){
die "Cannot open the file";
}
foreach $row (#content){
if ($row =~ /def="([A-Z\/:A-Z]+)"/g){
$def = $1
}
if ($row =~ /pattern="([A-Z\s]+)"/){
$seqs = $1;
}
}
print(WRITE_HANDLE $seqs);
The problems I encountered with this code are:
It only prints the last pattern into file_OUT.txt
I don't know how to print the header and the pattern consecutively for each site.
Keep the header in a variable, print it when you encounter the pattern:
#!/usr/bin/perl
use warnings;
use strict;
my $def;
while (<>) {
$def = $1 if m{/def="CODE:(.*?)"};
print ">$def\n$1\n" if m{/pattern="(.*?)"};
}
Another answer, but while editing some minor details in the posted script
use strict;
use warnings;
if(!open(MY_HANDLE, "file_IN.txt")){
die "Cannot open the file";
}
my #content = <MY_HANDLE>;
close(MY_HANDLE);
if(!open(WRITE_HANDLE, ">>file_OUT.txt")){
die "Cannot open the file";
}
foreach my $row (#content){
chomp($row);
if ($row =~ /def="[A-Z]*\:([A-Z|0-9]+)"/g){
print (WRITE_HANDLE ">".$1."\n");
}
if ($row =~ /pattern="([A-Z\s]+)"/){
print (WRITE_HANDLE $1."\n");
}
}

Can't find a string in array

I have a file with almost 1,500 names of Marvel heroes, each name in new line. I have to ask user what his favourite hero is and find out if it's a hero from the list or not. Here's what I have right now. It doesn't work: I can guess only the last hero from the list. For the rest it just prints that they are not on the list.
print "Whats your favourite hero?\n";
my $hero = <stdin>;
chomp $hero;
open FILE, "<list_marvel.txt";
my #marvel = <FILE>;
chomp(#marvel);
my $result = 0;
foreach (#marvel) {
if ($_ eq $hero);
}
if ($result == 1) {
print "That hero is on the list";
}
else {
print "$hero is not on the list.\n";
}
Here are two files:
-Perl code : Perl Code
-List of heroes : List
Your program has a syntax error and won't compile. It certainly won't find only the last name on the list
The main problem is that you never set $result, and if($_ eq $hero) should be something like $result = 1 if($_ eq $hero)
You must always use strict and use warnings at the top of every Perl program you write. It is an enormous help in finding straighforward problems
Here's a working version
use strict;
use warnings;
my $filename = 'list_marvel.txt';
open my $fh, '<', $filename or die qq{Unable to open "'list_marvel.txt'": $!};
print "Whats your favourite hero? ";
my $hero = <>;
chomp $hero;
my $found;
while ( <$fh> ) {
chomp;
if ( $_ eq $hero ) {
++$found;
last;
}
}
print $found ? "$hero is on the list\n" : "$hero is not on the list";
You don't set $result anywhere to true.
Make your foreach loop like this:
foreach(#marvel){
$result = $_ eq $hero;
}
or
foreach (#marvel){
$result = 1 if $_ eq $hero
}
You forgot to increment your $result. If you indent your code properly, it is easier to see.
foreach (#marvel) {
# here something is missing
if ( $_ eq $hero );
}
Add $result++ if $_ eq $hero; in the foreach.
You should always use strict and use warnings. That would have told you about a syntax error near );.
Also consider using the three argument open with lexical filehandles.
Rewritten it looks like this:
use strict;
use warnings;
use feature 'say'; # gives you say, which is print with a newline at the end
say "What's you favourite hero?";
my $hero = <STDIN>;
chomp $hero;
# alsways name variables so it's clear what they are for
my $found = 0;
# die with the reason of error if something goes wrong
open my $fh, '<', 'list_marvel.txt' or die $!;
# read the file line by line
while ( my $line = <$fh> ) {
chomp $line;
if ( $line eq $hero ) {
# increment so we know we 'found' the hero in the list
$found++;
# stop reading at the first hit
last;
}
}
close $fh;
# no need to check for 1, truth is enough
if ( $result ) {
say "That hero is on the list.";
}
else {
say "$hero is not on the list.";
}
First, you miss setting the $result at around if($_ eq $hero).
Then, you may wish to make you comparison case insensitive. This would require a regular expression, e.g.:
$result = 1 if (/^$hero$/i);
Just modified your code. After if condition increment $result. Always use use strict and use warnings and always use 3 arguments to open a file.
use strict;
use warnings;
print "Whats your favourite hero?\n";
my $hero = <stdin>;
chomp $hero;
open FILE, "<", "list_marvel.txt" or die $!;
chomp (my #marvel = <FILE>);
close FILE;
my $result = 0;
foreach my $name (#marvel)
{
if($name eq $hero)
{
$result++;
}
}
if ($result == 1)
{
print "That hero is in the list.\n";
}
else
{
print "$hero is not in the list.\n";
}
This will take a single user entry from STDIN. It will run through the file of hero names, and if one matches the user entry it will print the name and exit the loop. If the name is not found it will tell you:
use warnings;
use strict;
open my $file1, '<', 'input.txt' or die $!;
print "Enter hero: ";
chomp(my $hero = <STDIN>);
my $result = 0;
while(<$file1>){
chomp;
if (/$hero/){
print "$_\n";
$result++;
last;
}
}
print "hero not in list\n" if $result == 0;

Printing value from split result Perl

Here I have a abc.txt file:
aaa,1000,kevin
bbb,2000,john
ccc,3000,jane
ddd,4000,kevin
Then I want to print out:
kevin
john
jane
my Perl script is:
open (INFILE, $ARGV[1]) or die "An input file is required as argument\n";
#store=();
while(<INFILE>)
{
chomp();
#data=split(/,/);
#
#
#
if (%store ne "0")
{
print "Printing users:\n";
foreach $key (keys %store)
{print $key."\n";}
}
print "Printing users:\n";
foreach $key (keys %store)
{print $key."\n";}
}
My idea is to store the value into hash and create key to each value. How can I do in the ### line?
You have declared #store and then using %store. I didn't understand that why you doing that, but the below code will give you desire output. First read the input file, split the data and then remove the duplicates.
use strict;
use warnings;
my $infile = $ARGV[0];
open my $fh, "<", $infile or die "An input file is required as argument: $!";
my %store;
while(my $line = <$fh>)
{
chomp($line);
my #data = split /,/, $line;
my #removeduplicate = (grep { !$store{$_}++ } #data)[2];
foreach(#removeduplicate){
if( $_ ne ''){
print "$_\n";
}
}
}
close $fh;
Output:
kevin
john
jane
hmmm. it depends what do you want. maybe this example will help you:
#!/usr/bin/perl
use strict;
use warnings;
use Data::Dumper; #for debug if you want
my $infile='abc.txt'; #or ARGV[0] whatever it is
my $fh;
open $fh,'<',$infile or die "problem with $infile $# $!";
my $inputline;
my %Storage;
my #Values;
while (defined($inputline=<$fh>)) {
chomp $inputline;
#Values=split ',',$inputline;
if (#Values != 3) {
warn "$inputline has formatted badly";
next;
}
#warn if exists $Storage{$Values[1]}; #optional warning for detected duplicates
$Storage{$Values[1]}=#Values[0,2]; #create hash data
#duplicates will be removed automaticly
}
close $fh;
print Dumper \%Storage; #print how perl it stores
foreach my $Key (keys %Storage) { #example loop
print #{Storage->{$Key}},"\n"; #do anything
}
I hope this template will be enough for you.

open a file and replace a word using perl

I want to open a file and replace a word from a file.
My code is attached here.
open(my $fh, "<", "pcie_7x_v1_7.v") or die "cannot open <pcie_7x_v1_7.v:$!";
while (my $line = <$fh>) {
if ($line =~ timescale 1 ns) {
print $line $msg = "pattern found \n ";
print "$msg";
$line =~ s/`timescale 1ns/`timescale 1ps/;
}
else {
$msg = "pattern not found \n ";
print "$msg";
}
}
File contains pattern timescale 1ns/1ps.
My requirement is to replace timescale 1ns/1ps to be replaced with timescale 1ps/1ps.
At present else condition occurs always.
Update code after receiving comment:
Hi,
Thanks for the quick solution.
I changed the code accordingly, but the result was not successful.
I have attached the updated code here.
Please suggest me if I missed anything here.
use strict;
use warnings;
open(my $fh, "<", "pcie_7x_v1_7.v" )
or die "cannot open <pcie_7x_v1_7.v:$!" ;
open( my $fh2, ">", "cie_7x_v1_7.v2")
or die "cannot open <pcie_7x_v1_7.v2:$!" ;
while(my $line = <$fh> )
{
print $line ;
if ($_ =~ /timescale\s1ns/ )
{
$msg = "pattern found \n " ;
print "$msg" ;
$_ =~ s/`timescale 1ns/`timescale 1ps/g ;
}
else
{
$msg = "pattern not found \n " ;
print "$msg" ;
}
print $fh2 $line ;
}
close($fh) ;
close($fh2) ;
Result:
pattern not found
pattern not found
pattern not found
pattern not found
Regards,
Binu
3rd update:
// File : pcie_7x_v1_7.v
// Version : 1.7
//
// Description: 7-series solution wrapper : Endpoint for PCI Express
//
//--------------------------------------------------------------------------------
//`timescale 1ps/1ps
`timescale 1ns/1ps
(* CORE_GENERATION_INFO = "pcie_7x_v1_7,pcie_7x_v1_7,
You can use a perl oneliner from a command line. No need to write a script.
perl -p -i -e "s/`timescale\s1ns/`timescale 1ps/g" pcie_7x_v1_7.v
-
However,
If you still want to use the script, you are almost there. You just need to fix a couple errors
print $line; #missing
if ($line =~ /timescale\s1ns/) #made it a real regex, this should match now
$line =~ s/`timescale 1ns/`timescale 1ps/g ; #added g to match all occurences in line
after the if-else you must print the line to a file again
for example, open a new file for writing (let's call it 'pcie_7x_v1_7.v.2') at the beginning of your script
open(my $fh2, ">", "pcie_7x_v1_7.v.2" ) or die "cannot open <pcie_7x_v1_7.v.2:$!" ;
then , after the else block just print the line (whether it's changed or not) to the file
print $fh2 $line;
Don't forget to close the filehandles when you're done
close($fh);
close($fh2);
EDIT:
Your main problem was that you used $_ for the check, while you had assigned the line to $line. So you did print $line, but then if ($_ =~ /timescale/. That would never work.
I'm copy pasting your script and made a couple corrections and formatted it a little more dense to better fit in the website. I also removed the if match check as suggested by TLP and directly did the substitution in the if. It has exactly the same result. This works:
use strict;
use warnings;
open(my $fh, "<", "pcie_7x_v1_7.v" )
or die "cannot open <pcie_7x_v1_7.v:$!" ;
open( my $fh2, ">", "pcie_7x_v1_7.v2")
or die "cannot open >pcie_7x_v1_7.v2:$!" ;
while(my $line = <$fh> ) {
print $line;
if ($line =~ s|`timescale 1ns/1ps|`timescale 1ps/1ns|g) {
print "pattern found and replaced\n ";
}
else {
print "pattern not found \n ";
}
print $fh2 $line ;
}
close($fh);
close($fh2);
#now it's finished, just overwrite the old file with the new file
rename "pcie_7x_v1_7.v2", "pcie_7x_v1_7.v";

How to read from a file and direct output to a file if a file name is given in the command line, and printing to console if no argument given

I made a file, "rootfile", that contains paths to certain files and the perl program mymd5.perl gets the md5sum for each file and prints it in a certain order. How do I redirect the output to a file if a name is given in the command line? For instance if I do
perl mymd5.perl md5file
then it will feed output to md5file. And if I just do
perl mydm5.perl
it will just print to the console.
This is my rootfile:
/usr/local/courses/cs3423/assign8/cmdscan.c
/usr/local/courses/cs3423/assign8/driver.c
/usr/local/courses/cs3423/assign1/xpostitplus-2.3-3.diff.gz
This is my program right now:
open($in, "rootfile") or die "Can't open rootfile: $!";
$flag = 0;
if ($ARGV[0]){
open($out,$ARGV[0]) or die "Can't open $ARGV[0]: $!";
$flag = 1;
}
if ($flag == 1) {
select $out;
}
while ($line = <$in>) {
$md5line = `md5sum $line`;
#md5arr = split(" ",$md5line);
if ($flag == 0) {
printf("%s\t%s\n",$md5arr[1],$md5arr[0]);
}
}
close($out);
If you don't give a FILEHANDLE to print or printf, the output will go to STDOUT (the console).
There are several way you can redirect the output of your print statements.
select $out; #everything you print after this line will go the file specified by the filehandle $out.
... #your print statements come here.
close $out; #close connection when done to avoid counfusing the rest of the program.
#or you can use the filehandle right after the print statement as in:
print $out "Hello World!\n";
You can print a filename influenced by the value in #ARGV as follows:
This will take the name of the file in $ARGV[0] and use it to name a new file, edit.$ARGV[0]
#!/usr/bin/perl
use warnings;
use strict;
my $file = $ARGV[0];
open my $input, '<', $file or die $!;
my $editedfile = "edit.$file";
open my $name_change, '>', $editedfile or die $!;
if ($input eq "md5file"){
while ($in){
# Do something...
print $name_change "$_\n";
}
}
Perhaps the following will be helpful:
use strict;
use warnings;
while (<>) {
my $md5line = `md5sum $_`;
my #md5arr = split( " ", $md5line );
printf( "%s\t%s\n", $md5arr[1], $md5arr[0] );
}
Usage: perl mydm5.pl rootfile [>md5file]
The last, optional parameter will direct output to the file md5file; if absent, the results are printed to the console.