perl: unable to open a file for reading sometimes - perl

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

Related

how to read .evtx files?

I would like to read the contents of the Event Log on Windows using the Perl script. I can read the contents of the 'Application' Log. I can't read old logs - .evtx files. Can you advise me where I have a mistake?
$filename = "C:/Windows/System32/winevt/Logs/Archive-Application-2022-10-26-16-18-53-831.evtx";
if (-f $filename) {
printf "%s ... continue ...\n", $filename;
} else {
printf "PROBLEM\n";
exit -1;
}
$EventLog = new Win32::EventLog($filename) || die $!;
$EventLog->GetOldest($first) || die $!; # it dies here
EDIT:
Function GetOldest returns a RecordNumber. I tried UNC:
$filename = "\\\\<server>\\C\$\\Windows\\System32\\winevt\\Logs\\Archive-Application-2022-10-25-04-01-56-731.evtx";
... but the same error. The file exists but died on the GetOldest function. I haven't found something like $EventLog->errstr anywhere on the internet ...
https://metacpan.org/pod/Win32::EventLog
I tried a powerful thing: I canceled the error test:
originally : #$EventLog->GetOldest($first) || die $!;
now : $EventLog->GetOldest($first);
... and continue with the script; the script reads the contents of the .evtx file correctly.

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

Check whether a file is opened in Excel using Perl

I am currently able to check whether a CSV file with .csv extension is open by using this code
if (-e $filename) {
if (open(TXT,">>$filename")){
#file is closed
}
else {
#file is open
}
}
If I try this with a .txt file instead of a .csv file, then the test fails. It fails means the code executes if statement even when the file is opened by me whereas it should have executed else statement of the code.
But if I open a .txt file using Excel, it fails too.
How can I ensure that the test is successful if the file is opened using Notepad or Excel?
I checked another post How do you check if a file is open using Perl? but it didn't work for me. Can someone please suggest how to work on this.
UPDATE:
if (-e $filename) {
if (open(TXT,">>$filename")){
#file is not in use
} else {
#file is in use
}
If i replace the above code with the one mentioned in the answer
open TF, "<$filename" or die "unable to open file $!"; #open the existing file
if(<TF>){
close TXT;
} else {
goto END;
}
It doesnot work. maybe i did a syntax error on line where we open the existing file. ?
try this or you can use the other one also for same thing
open TF, "<test.txt" or die "unable to open file $!"; #open the existing test.txt file
if(<TF>)
{
print "file is open";
}
else
{
print "There might an issue with this file";
}
OR
if you want to check whether a file handler is open or not try this
open TF, ">>test1.txt" or die "unable to open file $!";
if(tell(TF) != -1)
{
print "file is open";
}
else
{
print "There might an issue with this file";
}
tell reports the position where you are in the file . If it's -1 which is an invalid position means that you aren't anywhere in the file.

Perl While Statement

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.

Perl Catch Variable in Error

I have a Perl script and I am trying to make it print out the value for $article when it errors. The script looks like:
eval{
for my $article($output =~ m/<value lang_id="">(.*?)<\/value>/g)
{
$article =~ s/ /+/g;
$agent->get("someurl");
$agent->follow_link(url_regex => qr/(?i:pdf)/ );
my $pdf_data = $agent->content;
open my $ofh, '>:raw', "$article.pdf"
or die "Could not write: $!";
print {$ofh} $pdf_data;
close $ofh;
sleep 10;
}
};
if($#){
print "error: ...: $#\n";
}
So if there is no .pdf file the code sends an error which is what I want. But what I need to know is it somehow possible to get the name of the $article that caused the error? I was trying to use some kind of global variable with no luck.
Why don't you put the eval inside the for loop? Something like this:
for my $article($output =~ m/<value lang_id="">(.*?)<\/value>/g)
{
$article =~ s/ /+/g;
eval{
# ...
}
if ($#) {
print STDERR "Error handling article: ", $article, " ", $!, "\n";
}
}
If that's your only problem, just declare my $article; before the eval, and remove the my from the for loop. But from your reply to Cornel Ghiban, I suspect it isn't.
Include the file name in the die>/ string:
open my $ofh, '>:raw', "$article.pdf" or die "Could not write '$article': $!";
I assume that you want to write and not read. Unless you have a permission issue or a full file system, a write is likely to succeed and you will never see an error.
Your script does not need to die, you can just set a flag or save message to the log or store error for late handling.
my #errors=();
................
open my $ofh, '>:raw', "$article.pdf" or do { push #errors,"$article: $!" };
if(-e $ofh) {
# work with the file
}
................
if(#errors) {
# do something
}