perl open file error handling - perl

I want to do some task when the file is not opened in the Perl program below. But when I run it, I am getting syntax errors. What's wrong with it?
my $LOGPATH = $ENV{DATA_OU};
my $LOGFILE = "cdj_rep" . "." . "test" . ".rpt";
if ! (open(OUT,">$LOGPATH/test1/work/$LOGFILE")) {
print "testin";
return;
}
close(OUT);

I'd write it as
my $LOGPATH = $ENV{DATA_OU};
my $LOGFILE = "cdj_rep.test.rpt";
my $path = "$LOGPATH/test1/work/$LOGFILE";
open my $fh, ">", $path or do {
warn "$0: open $path: $!";
return;
};
close $fh or warn "$0: close $path: $!";
Place the entire path in $path so you don't have to repeat it multiple times, and if you ever need to change it, you can do so in one place.
The call to open uses a lexical filehandle (my $fh) rather than a bareword handle. This is a good habit to develop because passing $fh to subs or stuffing it inside data structures tends to be more natural syntactically.
It also uses the 3-argument form of open so you don't have to worry about characters in the path being interpreted specially. It may not seem like a big deal in the context of your code, but it's another good habit to develop.
A common idiom for checking whether an open succeeds is
open my $fh, "<", $path
or die "$0: open $path: $!";
Using if (!open ... or unless (open ... would be fine, but with a lexical filehandle, you need to worry about scoping issues. You seem to be using the check as a guard, so writing open or ... leaves the filehandle in scope for when it does succeed. You want two statements to execute if it fails, so you need to wrap them in do { ... } as above.
Also note the content of the error message passed to warn:
the program that had an error ($0)
what it was trying to do (open $path)
and why it failed ($!)
The warn and die operators send their output to the standard error, which allows the flexibility of redirecting error messages elsewhere.
Finally, when you close a handle on a file you created to write or append, you should check whether it fails, which might happen if an I/O error occurred, for example.

The ! needs to go inside the brackets:
if (! open (out,

Related

Printing a text file using perl

I am completely new to this and this should be the easiest thing to do but for some reason I cannot get my local text file to print. After trying multiple times with different code I came to use the following code but it doesn't print.
I have searched for days on various threads to solve this and have had no luck. Please help. Here is my code:
#!/usr/bin/perl
$newfile = "file.txt";
open (FH, $newfile);
while ($file = <FH>) {
print $file;
}
I updated my code to the following:
#!/user/bin/perl
use strict; # Always use strict
use warnings; # Always use warnings.
open(my $fh, "<", "file.txt") or die "unable to open file.txt: $!";
# Above we open file using 3 handle method
# or die die with error if unable to open it.
while (<$fh>) { # While in the file.
print $_; # Print each line
}
close $fh; # Close the file
system('C:\Users\RSS\file.txt');
It returns the following: my first report generated by perl. I do not know where this is coming from. Nowhere do I have a print "my first report generated by perl."; statement and it definitely is not in my text file.
My text file is full of various emails, addresses, phone numbers and snippets of emails.
Thank you all for your help. I figured out my problem. I somehow managed to kick myself out of my directory and did not realize it.
This is most likely a combination of a failure to open the file, and a failure to check the return value of open.
If you are completely new to perl, I warmly recommend reading the excellent "perlintro" man page, using either man perlintro or perldoc perlintro on the command line, or taking a look here: https://perldoc.perl.org/perlintro.html.
The "Files and I/O" section there gives a good and concise way of doing this:
open(my $in, "<", "input.txt") or die "Can't open input.txt: $!";
while (<$in>) { # assigns each line in turn to $_
print "Just read in this line: $_";
}
This version will give you an explanation and abort if anything goes wrong while trying to open the file. For example, if there is no file named file.txt in the current working directory, your version will quietly fail to open the file, and afterwards it will quietly fail to read from the closed file handle.
Also, always adding at least one of these to your perl scripts will save you a lot of trouble in the long run:
use warnings; # or use the -w command line switch to turn warnings on globally
use diagnostics;
These won't catch the failure to open the file, but will alert on the failed read.
In the first example here you can see that without the diagnostics module, the code fails without any error messages. The second example shows how the diagnostics module changes this.
$ perl -le 'open FH, "nonexistent.txt"; while(<FH>){print "foo"}'
$ perl -le 'use diagnostics; open FH, "nonexistent.txt"; while(<FH>){print "foo"}'
readline() on closed filehandle FH at -e line 1 (#1)
(W closed) The filehandle you're reading from got itself closed sometime
before now. Check your control flow.
By the way, the legendary "Camel Book" is basically the perl man pages formatted for paper printing, so reading the perldocs in the order listed in perldoc perl will give you a high level of understanding of the language in a reasonably accessible and inexpensive manner.
Happy hacking!
This is simple and including explanations.
use strict; # Always use strict
use warnings; # Always use warnings.
open(my $fh, "<", "file.txt") or die "unable to open file.txt: $!";
# Above we open file using 3 handle method
# or die die with error if unable to open it.
while (<$fh>) { # While in the file.
print $_; # Print each line
}
close $fh; # Close the file
There is then also the case where you are trying to open a file which is not in a location where you think it is. So consider doing full path, if not in the same dir.
open(my $fh, "<", 'F:\Workdir\file.txt') or die "unable to open < input.txt: $!";
EDIT: After your comments, it seems that you are opening an empty file. Please add this at the bottom of that same script and rerun. It will open the file in C:\Users\RSS and make sure it does actually contain data?
system('C:\Users\RSS\file.txt');
First, of all as you are starting out, it is better to enable all warnings by 'use warnings' and disable all such expression which can lead to uncertain behavior or are difficult to debug by pragma 'use strict'.
As you are dealing with file stream, it is always recommended to the check if you were able to open the stream. so, try to use croak or die both would terminate the program with a given message.
Instead of reading inside the while condition, I would recommend checking for end of file. So, loop breaks as end is found. Usually, when reading a line you would use it for further processing, so it is good idea to remove end of lines using chomp.
A sample for reading a file in perl can be as follows:
#!/user/bin/perl
use strict;
use warnings;
my $newfile = "file.txt";
open (my $fh, $newfile) or die "Could not open file '$newfile' $!";
while (!eof($fh))
{
my $line=<$fh>;
chomp($line);
print $line , "\n";
}

Perl: open or [block of code]?

I'm running script A which feeds ARGV containing the path to a file to perl script B. This is done by a
local #ARGV = ($file, $file2, etc.);
do scriptB.pl or die "scriptB has failed";
Script B then tries to open the file:
open( my $fh_file, "<", $file )
or die "Could not open file '$file' $!";
However, if the file is missing I do not get the message quoted after "or die" in B. Instead I get the do scriptB.pl or die message in A. If I remove the "or die" from A, the script continues after B silently dies as if nothing went wrong.
I was wondering if there was any way to get B to print its die message?
Better yet, what is the best way to have B run a block of code after it fails to open the file? Said code would for example write to a separate file listing which files were missing so that the user may easily track down such errors.
#something like
open( my $fh_file, "<", $file) or {
print "the file could not be found";
die;
}
The only thing I've found searching the net for help was someone mentioning a "or do {}", but this is giving me strange syntax errors so I am not sure if I'm using it right.
If you want to continue to use the open(...) or ... syntax, then you could use do.
open my $fh, '<', $file or do {
...
};
But I think it's probably clearer to switch to if
if (! open my $fh, '<', $file) {
...
}
Or even unless
unless (open my $fh '<', $file) {
...
}
I think you'll get clearer code with fewer gotchas if you put script B into a module, and load it with use or require and call the function(s) in there directly with clear parameters.
What you're missing here is that do involves an eval behind the scenes, and that results in the exception confusion. You can more or less avoid that confusion by moving your script B code into a function in a module, and calling it.
(Also, perl 5.26 will have a slight hiccup with do wherein the current directory will be removed from the directory lookup, due to security concerns. use and require have the same hiccup, but this may be less surprising since you should put your module into a path you explicitly get into the #INC load path.)
die doesn't print a message; die throws an exception. When you catch that exception you don't do anything with the message passed to die. Replace
local #ARGV = ($file, $file2, etc.);
do scriptB.pl or die "scriptB has failed";
with
local #ARGV = ($file, $file2, etc.);
do scriptB.pl or die "scriptB has failed: ". ( $# || $! );

Does die have to be used if opening a file fails?

Most of time, I do something like this:
open FH, ">file.txt" or die "Cann't open file: $!";
Does die have to be used? If I want my script to continue (and simply ignore the error if the file cannot be opened), what should I do?
You might want to do something like
if(open my $fh, ">", "file.txt") {
# do stuff with file
close $fh;
}
else {
# do stuff without file
}
The code doesn't have to die if it cannot access the file.
If writing to the file is optional, you might do something like this:
my $file_is_ok = 0;
open FH, ">file.txt" and $file_is_ok = 1;
warn "No logging available" unless $file_is_ok;
Then wherever you want to use the file handle, you can check first that it is ok:
$file_is_ok and print FH "Something happened";
Usually you'll want a little more abstraction than the above, but I hope that is enough to get started.
No, die most definitely doesn't have to be used, or else a failed open would simply exit the program. This is just a commonly used construct since often if you can't open a file there's no point in continuing, but there really is no requirement to die or to do anything else either.
open(my $fh, ">", $file) or print "open failed , you are on your own\n";
I might do something like:
sub process_file {
my $file = shift;
open(my $fh, ">", $file) or return;
# write to file ...
}
It is recommended that you use croak() from Carp instead, especially in modules or objects. See http://perldoc.perl.org/Carp.html
Carp is a standard module and, like all standard modules, comes installed with Perl. A list of all the standard modulesis available in perlmodlib. See http://perldoc.perl.org/perlmodlib.html
Sorry for responding to a few months old question, but the way I do this is by implementing my own die style subroutine:
sub fail {
my $text = $_[0];
# Do error-related stuff here, like send an email
die($text) # or just exit
}
open(my $fh, ">file.txt") or fail("Unable to open file: $!");
You could use a try catch block, using Try::Tiny or something of the sort.
If what you are trying to do is avoid exiting the program when you encounter an error opening a file, omitting the die is not the correct approach. die is Perl's exception mechanism; the fact that it aborts the program by default is an accidental, not fundamental, property. If you supply an exception handler, you retain control. The correct approach is to catch the exception and do your cleanup in the finally block.
use 5.10.0;
use Try::Tiny;
my ($infile, $FH);
try {
open $infile, '<', 'infile.txt' or die "Can't open infile";
try {
open $FH, '>', 'file.txt' or die "Can't open outfile";
my $line = <$infile>;
print $FH $line;
say 'Cleaning up $FH';
close $FH;
} finally {
say 'Cleaning up $infile';
close $infile;
} catch {
die $_;
};
} catch {
die $_;
};
So if open $infile ... fails, die to the catch block, which redies and aborts the script. But once we've opened $infile, if open $FH fails, die to a catch block that also aborts the program but forces close $infile to happen first. File handles aren't the best examples of resources to clean up before program exit because the interpreter closes files automatically, but the basic idea is there.
If you don't like the nested try blocks, you can achieve a very similar effect by checking which exception caused the block to abort and deciding what cleanup is necessary based on that. That's a little more fragile, but still more robust than anything mentioning goto.

File truncated, when opened in Perl

Im new to perl, so sorry if this is obvious, but i looked up how to open a file, and use the flags, but for the life of me they dont seem to work right I narrowed it down to these lines of code.
if ($flag eq "T"){
open xFile, ">" , "$lUsername\\$openFile";
}
else
{
open xFile, ">>", "$lUsername\\$openFile";
}
Both of these methods seem to delete the contents of my file. I also checked if the flag is formatted correctly and it is, i know for a fact ive gone down both conditions.
EDIT: codepaste of a larger portion of my code http://codepaste.net/n52sma
New to Perl? I hope you're using use strict and use warnings.
As other's have stated, you should be using a test to make sure your file is open. However, that's not really the problem here. In fact, I used your code, and it seems to work fine for me. Maybe you should try printing some debugging messages to see if this is doing what you think it's doing:
use strict;
use warnings;
use autodie; #Will stop your program if the "open" doesn't work.
my $lUsername = "ABaker";
my $openFile = "somefile.txt";
if ($flag eq "T") {
print qq(DEBUG: Flag = "$flag": Deleting file "$lUsername/$openFile");
open xFile, ">" , "$lUsername/$openFile";
}
else {
print qq(DEBUG: Flag = "$flag": Appending file "$lUsername/$openFile");
open xFile, ">>", "$lUsername/$openFile";
}
You want to use strict and warnings in order to make sure you're not having issues with variable names. The use strict forces you to declare your variables first. For example, are you setting $Flag, but then using $flag? Maybe $flag is set the first time through, but you're setting $Flag the second time through.
Anyway, the DEBUG: statements will give you a better idea of what your error could be.
By the way, in Perl, you're checking if $flag is set to T and not t. If you want to test against both t and T, test whether uc $flag eq 'T' and not just $flag eq 'T'.
#Ukemi
I reformated to comply with use strict, i also made print statements to make sure i was trunctating when i want to, and not when i dont. It still is deleting the file. Although now sometimes its simply not writing, im going to give a larger portion of my code in a link, id really appreciate it if you gave it a once over.
Are you seeing it say Truncating, but the file is empty? Are you sure the file already existed? There's a reason why I put the flag and everything in my debug statements. The more you print, the more you know. Try the following section of code:
$file = "lUsername/$openFile" #Use forward slashes vs. back slashes.
if ($flag eq "T") {
print qq(Flag = "$flag". Truncating file "$file"\n);
open $File , '>', $file
or die qq(Unable to open file "$file" for writing: $!\n);
}
else {
print qq(Flag = "$flag". Appending to file "$file"\n);
if (not -e $file) {
print qq(File "$file" does not exist. Will create it\n");
}
open $File , '>>', $file
or die qq(Unable to open file "$file" for appending: $!\n);
}
Note I'm printing out the flag and the name of the file in quotes. This will allow me to see if there are any hidden characters in my file name.
I'm using the qq(...) method to quote strings, so I can use the quotation marks in my print statements.
Also note I'm checking for the existence of the file when I truncate. This way, I make sure the file actually exists.
This should point out any possible errors in your logic. The other thing you can do is to stop your program when you finish writing out the file and verify that the file was written out as expected.
print "Write to file now:\n";
my $writeToFile = <>;
printf $File "$writeToFile";
close $File;
print "DEBUG: Temporary stop. Examine file\n";
<STDIN>; #DEBUG:
Now, if you see it saying it's appending to the file, and the file exists, and you still see the file being overwritten, we'll know the problem lies in your actual open xFile, ">>" $file statement.
You should use the three-argument-version of open, lexical filehandles and check wether there might have been an error:
# Writing to file (clobbering it if it exists)
open my $file , '>', $filename
or die "Unable to write to file '$filename': $!";
# Appending to file
open my $file , '>>', $filename
or die "Unable to append to file '$filename': $!";
>> does not clobber or truncate. Either you ended up in the "then" clause when you expected to be in the "else" clause, or the problem is elsewhere.
To check what $flag contains:
use Data::Dumper;
local $Data::Dumper::Useqq = 1;
print(Dumper($flag));
For your reference I have mentioned some basic file handling techniques below.
open FILE, "filename.txt" or die $!;
The command above will associate the FILE filehandle with the file filename.txt. You can use the filehandle to read from the file. If the file doesn't exist - or you cannot read it for any other reason - then the script will die with the appropriate error message stored in the $! variable.
open FILEHANDLE, MODE, EXPR
The available modes are the following:
read < #this mode will read the file
write > # this mode will create the new file. If the file already exists it will truncate and overwrite.
append >> #this will append the contents if the file already exists,else it will create new one.
if you have confusion on this, you can use the module called File::Slurp;
I have mentioned the sample codes using File::Slurp module.
use strict;
use File::Slurp;
my $read_mode=read_file("test.txt"); #to read file contents
write_file("test2.txt",$read_mode); #to write file
my #all_files=read_dir("/home/desktop",keep_dot_dot=>0); #read a dir
write_file("test2.txt",{append=>1},"#all_files"); #Append mode

How do I resolve a "print() on closed filehandle" error in Perl?

I am getting this error while executing my Perl script. Please, tell me how to rectify this error in Perl.
print() on closed filehandle MYFILE
This is the code that is giving the error:
sub return_error
{
$DATA= "Sorry this page is corrently being updated...<p>";
$DATA.= " Back ";
open(MYFILE,">/home/abc/xrt/sdf/news/top.html");
print MYFILE $DATA;
close(MYFILE);
exit;
}
I hope that now I'm clearer.
You want to do some action on MYFILE after you (or the interpreter itself because of an error) closed it.
According to your code sample, the problem could be that open doesn't really open the file, the script may have no permission to write to the file.
Change your code to the following to see if there was an error:
open(MYFILE, ">", "/home/abc/xrt/sdf/news/top.html") or die "Couldn't open: $!";
Update
ysth pointed out that -w is not really good at checking if you can write to the file, it only ‘checks that one of the relevant flags in the mode is set’. Furthermore, brian d foy told me that the conditional I've used isn't good at handling the error. So I removed the misleading code. Use the code above instead.
It appears that the open call is failing. You should always check the status when opening a filehandle.
my $file = '/home/abc/xrt/sdf/news/top.html';
open(MYFILE, ">$file") or die "Can't write to file '$file' [$!]\n";
print MYFILE $DATA;
close MYFILE;
If the open is unsuccessful, the built-in variable $! (a.k.a. $OS_ERROR) will contain the OS-depededant error message, e.g. "Permission denied"
It's also preferable (for non-archaic versions of Perl) to use the three-argument form of open and lexical filehandles:
my $file = '/home/abc/xrt/sdf/news/top.html';
open(my $fh, '>', $file) or die "Can't write to file '$file' [$!]\n";
print {$fh} $DATA;
close $fh;
An alternate solution to saying or die is to use the autodie pragma:
#!/usr/bin/perl
use strict;
use warnings;
use autodie;
open my $fh, "<", "nsdfkjwefnbwef";
print "should never get here (unless you named files weirdly)\n";
The code above produces the following error (unless a file named nsdfkjwefnbwef exists in the current directory):
Can't open 'nsdfkjwefnbwef' for reading: 'No such file or directory' at example.pl line 7
This:
open(MYFILE,">/home/abc/xrt/sdf/news/top.html");
In modern Perl, it could be written as:
open(my $file_fh, ">", "/home/abc/xrt/sdf/news/top.html") or die($!);
This way you get a $variable restricted to the scope, there is no "funky business" if you have weird filenames (e.g. starting with ">") and error handling (you can replace die with warn or with error handling code).
Once you close $file_fh or simply go out of scope, you can not longer print to it.
I had this problem when my files were set to READ-ONLY.
Check this also, before giving up! :)
Check that the open worked
if(open(my $FH, ">", "filename") || die("error: $!"))
{
print $FH "stuff";
close($FH);
}
If you use a global symbol MYFILE as your filehandle, rather than a local lexical ($myfile), you will invariably run into issues if your program is multithreaded, e.g. if it is running via mod_perl. One process could be closing the filehandle while another process is attempting to write to it. Using $myfile will avoid this issue as each instance will have its own local copy, but you will still run into issues where one process could overwrite the data that another is writing. Use flock() to lock the file while writing to it.
Somewhere in you're script you will be doing something like:
open MYFILE, "> myfile.txt";
# do stuff with myfile
close MYFILE;
print MYFILE "Some more stuff I want to write to myfile";
The last line will throw an error because MYFILE has been closed.
Update
After seeing your code, it looks like the file you are trying to write to can't be opened in the first place. As others have already mentioned try doing something like:
open MYFILE, "> myfile.txt" or die "Can't open myfile.txt: $!\n"
Which should give you some feedback on why you can't open the file.