Perl While Statement - perl

Okay, so here's my final question (for the day): I am trying to get my program to search through a document. If the document has the word "unsuccessful" in it anywhere, then the program will search for the word "error" and record all instances of error. However, I am having a hard time making the two dependent on one another. Please help! I am very very new to Perl (this is only my second day using it) so the more detail/comments you can provide, the better! Here is my current code, I am aware it does not run right now:
#!/usr/local/bin/perl
my $argument1 = $ARGV[0];
my $argument2 = $ARGV[1];
open (LOGFILE, "<$argument1") or die "Can't find file";
open FILE, ">>$argument2" or die $!;
while (<LOGFILE>){
if {(/Unsuccessful/){
while(<LOGFILE>){
if (/Error/){
print FILE "ERROR in line $.\n" ;
}
}
}
}
}
close FILE;
close LOGFILE;

Check for "Unsuccessful" and "Error" in one loop and at the end print error findings if "Unsuccessful" has been found...
my $argument1 = $ARGV[0];
my $argument2 = $ARGV[1];
open (LOGFILE, "<$argument1") or die "Can't find file";
open (FILE, ">>$argument2") or die $!;
my $unsuccessful = 0;
my #errors = ();
while (<LOGFILE>) {
if (/Unsuccessful/i) {
$unsuccessful = 1;
}
if (/Error/i) {
push(#errors, "ERROR in line $.\n");
}
}
if ($unsuccessful) {
print $_ for #errors;
}
Switch /i applies for case-insensitive search, so remove it from the code above if not wanted.

Using
<LOGFILE>
multiple times is probably not what you want. The more immediate cause of your trouble is probably a badly placed "{".
It looks like you expect "Error" to always appear later than "Unsuccessful", right?
Try
my $argument1 = $ARGV[0];
my $argument2 = $ARGV[1];
open (LOGFILE, "<$argument1") or die "Can't find file";
open FILE, ">>$argument2" or die $!;
my $unsuccessful = 0;
while (<LOGFILE>){
if ($unsuccessful) {
if (/Error/) { print FILE "ERROR in line $.\n"; }
}
else {
if (/Unsuccessful/) { $unsuccessful = 1; }
}
}
close FILE;
close LOGFILE;

You are committing a grave mistake by taking step 5 before step 1. You're not using the strict and warning pragmas in your code. In fact, the code you posted doesn't compile.
As for the problem in question, provided that you want to parse each file only once (as a good programmer should strive to do), you should parse in two modes: (1) the mode where unsuccessful has been detected, and the mode where it has not yet been detected. The former will have the job of outputting lines, while the latter won't.
Now I'd suggest getting back to some basics and not taking steps in advance. I've taken step 5 before step 1 many times in the past myself, and it was a mistake each and every time.

Related

Writing to a file inside if statement not working in Perl

I've looked around here a bit and found similar questions but not exactly. If there is one, I apologize and please point me to it.
I have the following code. I'm trying to create a csv file of simply an ID pulled from a filename and the filename itself. This is the ENTIRE script.
use strict;
use warnings;
use File::Find;
find( \&findAllFiles, '.');
exit;
sub findAllFiles {
my #fp1;
my #fp2;
my $patId;
my $filename;
my $testvar = "hello again";
$filename = $File::Find::name;
if ($filename =~ /\.pdf$/) {
open (my $fh, '>', 'filenames.csv') or die "Failed to open - $!\n";
print $fh "starting...$testvar\n" or die "Failed to print to file - $!\n";
#fp1 = split('/', $filename);
#fp2 = split('_', $fp1[-1]);
$patId = $fp2[-1];
$patId =~ s/\.pdf$//;
print "Adding $patId, file = $filename\n";
print $fh "$patId,$filename\n" or die "File print error: $!";
close $fh or warn "close failed! - $!";
}
return;
}
The line that prints to the screen, prints perfectly.
If I take the file open/close and the first print statement out of the if block, it prints that line into the file, but not the data inside the block.
I've tried every combo I can think of and it doesn't work. I've alternated between '>' and '>>' since it clearly needs the append since it's looping over filenames, but neither works inside the if block.
Even this code above doesn't throw the die errors! It just ignores those lines! I'm figuring there's something obvious I'm missing.
Quoting File::Find::find's documentation:
Additionally, for each directory found, it will chdir() into that directory
It means that when you open inside findAllFiles, you are potentially opening a file filenames.csv inside a subdirectory of your initial directory. You can run something like find . -name filenames.csv from your terminal, and you'll see plenty of filenames.csv. You can change this behavior by passing no_chdir option to find:
find( { wanted => \&findAllFiles, no_chdir => 1}, '.');
(and additionally changing > for >> in your open)
However, personally, I'd avoid repeatedly opening and closing filenames.csv when you could open it just once before calling find. If you don't want to have your filehandle globally defined, you can always pass it as an argument to findAllFiles:
{
open my $fh, '>', 'filenames.csv' or die "Failed to open 'filenames.csv': $!";
find(sub { findAllFiles($fh) }, '.')
}
sub findAllFiles {
my ($fh) = #_;
...
filenames.csv will be created in the directory where the pdf is found, since find() changes directories as it searches. If that's not what you want, use an absolute path to open it (or open it before calling find, which seems like a better idea).

Cannot decode! Invalid Base58 Character(s)!

I am trying to run
base58perl.pl
in my terminal using the following command:
perl base58perl.pl
but I get the following error:
Cannot decode! Invalid Base58 Character(s)!
Here's the code:
my $fileSrc = 'base58.txt';
open my $fhSrc, $fileSrc or die "Could not open $fileSrc: $!";
my $fileDest = 'hex.txt';
open( my $fhDest, '>>', $fileDest) or die "Could not open file $fileDest: $!";
while ( my $base58_encoded_address = <$fhSrc >) {
my $binary_address = decodebase58tohex($base58_encoded_address);
say $fhDest $binary_address;
}
close $fhSrc;
close $fhDest;
The content of base58.txt is a list of BTC address in base58 form.
I also have tried
chmod a+x base58perl.pl
perl base58perl.pl
base58.txt contents:
1E5PBfSaFawBy1RjBHkS6FDtCwXkYSsVTo
1DCgptTS2uY2occbVdW1qcVT72T75RXbyg
1CUNEBjYrCn2y1SdiUMohaKUi4wpP326Lb
I still get the same error.
That error message comes from the unbase58 function in the code you have linked.
die "Cannot Decode! Invalid Base58 Character(s)!\n" unless $bitcoin_address =~ /^[1-9A-HJ-NP-Za-km-z]*$/;
That line checks if the input contains only characters of the character group [1-9A-HJ-NP-Za-km-z]. Since your input does, it must dislike something else.
My guess is that it disliked the newline characters at the end of your lines. You need to chomp them off before passing the value to decodebase58tohex.
while( my $base58_encoded_address = <$fhSrc>) {
chomp $base58_encoded_address;
my $binary_address = decodebase58tohex($base58_encoded_address);
say $fhDest $binary_address;
}
You probably need to remove whitespace. You appear to be passing only chunks of the string to the decode function at a time, which could also be a problem. Read the whole file into a var, remove any whitespace, then decode.
my $base58_encoded_address = do { local $/; <$fhSrc> };
$base58_encoded_address =~ s/\s+//g;
my $binary_address = decodebase58tohex($base58_encoded_address);
say $fhDest $binary_address;
my $fileSrc = 'base58.txt';
open my $fhSrc, $fileSrc or die "Could not open $fileSrc: $!";
my $fileDest = 'hex.txt';
open( my $fhDest, '>>', $fileDest) or die "Could not open file $fileDest: $!";
my #tmp = <$fhSrc>;
chomp #tmp;
for my $line (#tmp) {
print "decoding '$line'\n";
my $binary_address = decodebase58tohex($line);
say $fhDest $binary_address;
}
close $fhSrc;
close $fhDest;
As someone else mentioned I think your dealing with whitespaces.
chomp will take care of that for you.
The next thing to do is print the string you are trying to decode in quotes which will confirm your only decoding what you want to.
The script is now working properly, the problem was the base58.txt the file was created using notepad. I created a new file using a different text editor.

Perl File Write Issue

I'm having a really weird problem with this perl script. The basic point is that sometimes a file write/append doesn't happen. On a run of the program, either all of the writes will happen or none of them will. Here is the subroutine, with some comments:
sub process_svs {
my $F;
open($F, '<', $_[0]);
if($log_dups==1) {
open($dfh, '>>',"./duplicates.txt");
}
while (my $line = <$F>) {
chomp $line;
if($line =~ /somepattern/) {
if (! -e "somefile") {
copy("source","dest") or warn ("couldn't copy");
} elsif($log_dups==1) {
system("touch ./duplicates.txt"); # ghetto workaround
print $dfh "./".$_[0]."_files/".$1.",v already exists\n" or die("Couldn't write duplicate"); # problem line
}
}
}
close $F;
}
The print statements to stdout always work, but if I remove the touch ./duplicates.txt crap, nothing is written to duplicates.txt.
The other "weird" thing, is that earlier in the program, I create a directory with perl mkdir, and if the directory exists when the program is run, I don't need the workaround, the duplicates.txt writing works just fine. If I delete the directory, and let the program mkdir it, it doesn't work. Seems relevant, but I can't figure out how since the directory and the text file are not in the same location, or related in any way, that I can think of.
Additionally, I have run it through the debugger, and can see the write call being executed, but inspecting duplicates.txt immediately after the write shows nothing written.
Any possible reasons for this would be greatly appreciated.
If you want to see a modified, but more complete, version of the script, it is here:
use strict;
use warnings;
use File::Copy;
my $svs = $ARGV[0];
my $rhis_str = system("rhis $svs > ./tmp_history");
my $fh;
my $dfh;
my #versions;
my $all_revs = 0;
my $current_rev = "";
my $log_dups = 0;
sub process_svs {
my $F;
open($F, '<', $_[0]);
if($log_dups==1) {
open($dfh, '>>',"./duplicates.txt");
}
while (my $line = <$F>) {
chomp $line;
if($line =~ /something/) {
if (! -e "something") {
copy("source","dest") or warn ("couldn't copy ");
} elsif($log_dups==1) {
system("touch ./duplicates.txt"); # ghetto workaround
print $dfh "something already exists\n" or die("Couldn't write duplicate");
}
}
}
close $F;
}
for(my $i = 0; $i <= scalar #ARGV; $i++) {
my $arg = $ARGV[$i];
if($arg eq "-a") {
$all_revs = 1;
} elsif($arg eq "-r") {
$all_revs = 0;
$current_rev = $ARGV[$i+1];
} elsif($arg eq "--log-dups") {
$log_dups = 1;
}
}
open($fh, '<','./tmp_history') or die(">>> Failed to open ./tmp_history");;
mkdir "./".$svs."_files";
if($all_revs == 1) {
print ">>> Processing all revisions of ".$svs;
if($log_dups==1) {
print" (and logging duplicates)\n";
}
while(my $line = <$fh>) {
chomp $line;
if ($line =~ /something/) {
push #versions, $1;
}
}
}
system("some_cmd &>/dev/null");
process_svs($svs);
}
You're not checking to see if your files opened. This is a very, very basic mistake and you should fix this immediately. Either add or die $! after each open or, better yet, use autodie and it will take care of catching all IO exceptions for you and give you good, consistent error messages.
Most importantly, this will tell you why it failed to open. $! tells you why it failed. You don't have that in your check on print.
print $dfh "./".$_[0]."_files/".$1.",v already exists\n" or die("Couldn't write duplicate"); # problem line
You're checking if print failed, but you're not including $!. Either add $! like die "Couldn't write to duplicate: $!" or use autodie, remove the or die clause, and let autodie take care of it. I recommend the second.
I suspect you'll find that something else is deleting duplicates.txt between the open and the print.
The second thing that grabs my attention is here.
if($log_dups==1) {
open($dfh, '>>',"./duplicates.txt");
}
You're using a global variable $log_dups to decide whether or not to open the file for writing (and not checking if it succeeded). This should be a variable that gets passed into the function, it's just good programming practice. Later you decide whether to print to $dfh based on that global variable.
if (! -e "something") {
copy("source","dest") or warn ("couldn't copy ");
} elsif($log_dups==1) {
system("touch ./duplicates.txt"); # ghetto workaround
print $dfh "something already exists\n" or die("Couldn't write duplicate");
}
Because $log_dups is global it's possible something else is changing $log_dups between deciding to open duplicates.txt and writing to it. To avoid all these problems, and to make the code simpler, $log_dups should be an argument passed into the function.
Furthermore, the filehandle $dfh is inexplicably a global. Same problem, something else could be closing it. It will also not be automatically closed at the end of the function which might leave writes to duplicates.txt buffered until the program exits. $dfh should be a lexical.
Other problems...
my $rhis_str = system("rhis $svs > ./tmp_history");
$rhis_str will contain the exit status of the rhis program. I don't think that's what you want. You don't use this variable anyway.
There's no need to pass ./file to open, it's safe and easier to read to use just pass file. That it's in the current working directory is implied.
If you fix these basic problems and still have trouble, then edit your question with the revised code and we can look again.

Picking a specific line with a specific string

I am trying this in Perl to pick one complete line from whole document which contains "CURRENT_RUN_ID". I have been using below code to accomplish the above said task but I am unable to enter the while loop.
my $sSuccessString = "CURRENT_RUN_ID";
open(LOG, "$slogfile") or die("Can't open $slogfile\n");
my $sLines;
{
local $/ = undef;
$sLines=<LOG>;
}
my $spool = 0;
my #matchingLines;
while (<LOG>)
{
print OUTLOG "in while loop\n";
if (m/$sSuccessString/i) {
print OUTLOG "in if loop\n";
$spool = 1;
print map { "$_ \n" } #matchingLines;
#matchingLines = ();
}
if ($spool) {
push (#matchingLines, $_);
}
}
You are already done reading from the filehandle LOG after you have slurped it into $sLines. <LOG> in the head of the while will return undef because it has reached eof. You either have to use that variable $sLines in your while loop or get rid of it. You're not using it anyway.
If you only want to print the line that matches, all you need to do is this:
use strict;
use warnings;
open my $fh_in, '<', 'input_file' or die $!;
open my $fh_out '>', 'output_file' or die $!;
while (my $line = <$fh_in>) {
print $fh_out $line if $line =~ m/CURRENT_RUN_ID/;
}
close $fh_in;
close $fh_out;
When you execute this code:
$sLines=<LOG>;
it reads all of the data from LOG into $sLines and it leaves the file pointer for LOG at the end of the file. So when you next try to read from that file handle with:
while (<LOG>)
nothing is returned as there is no more data to read.
If you want to read the file twice, then you will need to use the seek() function to reset the file pointer before your second read.
seek LOG, 0, 0;
But, given that you never do anything with $sLines I suspect that you can probably just remove that whole section of the code.
The whole thing with $spool and #matchingLines seems strange too. What were you trying to achieve there?
I think your code can be simplified to just:
my $sSuccessString = "CURRENT_RUN_ID";
open(LOG, $slogfile) or die("Can't open $slogfile\n");
while (<LOG>) {
print OUTLOG if /$sSuccessString/i/;
}
Personally, I'd make it even simpler, by reading from STDIN and writing to STDOUT.
my $sSuccessString = 'CURRENT_RUN_ID';
while (<>) {
print if /$sSuccessString/i/;
}
And then using Unix I/O redirection to connect up the correct files.
$ ./this_filter.pl < your_input.log > your_output.log

perl: unable to open a file for reading sometimes

If a file in a directory matches some nomenclature, then it is to be processed.
I have problem with the following piece of code:
if ($fichier =~ /0284\-\d{4}-\w{6}\.0284\.UPDREQ\.\d{4}\.\d{10}/)
{
my $msg = "Processing file is : $fichier \n";
Trace($EXP, __FILE__, __LINE__, "$msg");
}
if(!open (FILE, "< $fichier"))
{
my $cmd = "mv $REP_FLOTS/$fichier $REP_UPDREQ_ARCH/err_$fichier";
system("$cmd");
}
$lines++ while (<FILE>);
close FILE;
It is able to open and read the content sometimes and it fails in other times.
What am I missing in this code? Because it is working fine sometimes.
Why don't you ask system itself about what's going wrong? Variable $! holds last error for previous system call, so if open failed, just print it:
if(!open (FILE, "< $fichier"))
{
warn "unable to open '$fichier' for reading: $!\n";
my $cmd = "mv $REP_FLOTS/$fichier $REP_UPDREQ_ARCH/err_$fichier";
system("$cmd");
}