Irregular Behavior with Perl print - perl

I'm attempting to print out to two different files. For some reason, print statements work fine for one file, but not for the other. When I run this program, filter2.out consists of a single line that reads "Beginning". filter2.err remains empty.
open(OUTPUT, "> Filter2/filter2.out");
open(ERROR, "> Filter2/filter2.err");
print OUTPUT "Beginning\n";
print ERROR "Beginning\n";
UPDATE: So I was running this at the beginning of a larger program and realized that it only updates the ERROR file in batches or when the file is closed. Any idea why this occurs?

Consider adding
use strict;
use warnings;
to the top of your script. These statements will help catch errors that are otherwise silently ignored by Perl. In addition, consider adding error checking to your open calls: in all likelihood, it's not actually opening. I'd write it like this:
use strict;
use warnings;
open(OUTPUT, "> Filter2/filter2.out")
or die "Can't open filter2.out: $!";
open(ERROR, "> Filter2/filter2.err")
or die "Can't open filter2.err: $!";
print OUTPUT "Beginning\n";
print ERROR "Beginning\n";
for example, by just adding adding strict and warnings I got:
print() on closed filehandle OUTPUT at .\printer.pl line 6.
print() on closed filehandle ERROR at .\printer.pl line 7.
Hmm...!
By adding error checking, I got:
PS C:\dev> perl .\printer.pl
Can't open filter2.out: No such file or directory at .\printer.pl line 4.
Aah! Looking, I didn't have the folder. After I added the folder, everything ran fine. You'll probably find something similar.
Finally, you should probably also use the modern, lexical file handles. This helps catch other errors (like re-used handle names.) Thus, the final script would look like:
use strict;
use warnings;
open(my $output, ">", "Filter2/filter2.out")
or die "Can't open filter2.out: $!";
open(my $error, ">", "Filter2/filter2.err")
or die "Can't open filter2.err: $!";
print $output "Beginning\n";
print $error "Beginning\n";
Viola! Now you can see exactly where the problem fails, as it fails, and make sure that other libraries or code you write later can't accidentally interfere with your file handles.

You need to check that your files were properly opened. Also it's better to use local variables as file handles instead of bare words:
open( my $err, "> Filter2/filter2.err") or die "Couldn't open error: $!"
print $err "Beginning\n"

Related

What is the easiest way to test error handling when writing to a file in Perl?

I have a bog standard Perl file writing code with (hopefully) adequate error handling, of the type:
open(my $fh, ">", "$filename") or die "Could not open file $filname for writing: $!\n";
# Some code to get data to write
print $fh $data or die "Could not write to file $filname: $!\n";
close $fh or die "Could not close file $filname afterwriting: $!\n";
# No I can't use File::Slurp, sorry.
(I just wrote this code from memory, pardon any typos or bugs)
It is somewhat easy to test error handling in the first "die" line (for example, create a non-writable file with the same name you plan to write).
How can I test error handling in the second (print) and third (close) "die" lines?
The only way I know of to induce error when closing is to run out of space on filesystem while writing, which is NOT easy to do as a test.
I would prefer integration test type solutions rather than unit test type (which would involve mocking IO methods in Perl).
Working with a bad filehandle will make them both fail
use warnings;
use strict;
use feature 'say';
my $file = shift || die "Usage: $0 out-filename\n";
open my $fh, '>', $file or die "Can't open $file: $!";
$fh = \*10;
say $fh 'writes ok, ', scalar(localtime) or warn "Can't write: $!";
close $fh or warn "Error closing: $!";
Prints
say() on unopened filehandle 10 at ...
Can't write: Bad file descriptor at ...
close() on unopened filehandle 10 at ...
Error closing: Bad file descriptor at ...
If you don't want to see perl's warnings capture them with $SIG{__WARN__} and print your messages to a file (or STDOUT), for example.
Riffing on zdim's answer ...
Write to a file handle opened for reading.
Close a file handle that has already been closed.

In Perl, why can't I open this file using single argument open?

The file is 52MB in size. It is in the same directory as the program.
$big = 'VIXhx.csv';
# tie #optLine, 'Tie::File', $big or die "Cant Tie to $big $!" ;
open $big or die "Cant open $big, $!," ;
Tie::File gave no error message.
Plain open gave the error message:
Cant open VIXhx.csv, No such file or directory, at C:\Python34\hsf\ETFs\VIX\qad.
pl line 47.
(Yes, it's in the Python directory - but Perl works fine there)
I can open the file in the editor, so there doesn't seem to be a problem with the file itself.
I have a smaller file in the same program that opened with no problem in Tie::File.
$dat = 'ETF5Y.csv';
tie #datLine, 'Tie::File', $dat or die "Cant Tie to $dat $!" ;
Is it possible that Perl is unable to open a file if it's too large?
Please check perldoc -f open on how to open files, what you did ended up in opening an empty filename,
strace perl -e '$big = "/etc/passwd"; open $big or die "Cant open $big, $!,"'
output
...
open("", O_RDONLY) = -1 ENOENT (No such file or directory)
write(2, "Cant open /etc/passwd, No such f"..., 64Cant open /etc/passwd, No such file or directory, at -e line 1.
See perldoc perlopentut:
Single Argument Open
Remember how we said that Perl's open took two arguments? That was a passive prevarication. You see, it can also take just one argument. If and only if the variable is a global variable, not a lexical, you can pass open just one argument, the filehandle, and it will get the path from the global scalar variable of the same name.
$FILE = "/etc/motd";
open FILE or die "can't open $FILE: $!";
while (<FILE>) {
# whatever
}
Therefore, if you want single argument open to do what you want, you would have to write our code as
$big = 'VIXhx.csv';
open big or die "Can't open '$big': $!";
# ^ <-- look, no dollar sign before filehandle
Alternatively, you could do something like this:
$big = 'VIXhx.csv';
*{$big} = \$big;
open $big and print <$big>;
if you want to keep the open $big.
But, relying on global variables and effects at a distance is not a good idea. Instead, use the three argument form of open to specify the filehandle, the mode, and the file name separately as in:
open my $vix_fh, '<', $vix_file
or die "Failed to open '$vix_file': $!";
By the way, you won't even find this section on "Single Argument Open" in recent Perl documentation. The following note should give you and idea why:
Why is this here? Someone has to cater to the hysterical porpoises. It's something that's been in Perl since the very beginning, if not before.
The single argument open can also be used to turn any Perl program into a quine.
I found the answer to my original question, of why TIE didn't work.
It turns out that the file used '0A' as the line terminator, so TIE, expecting '0D0A', read the whole 52MB file as one record.
I added recsep => "\n" to the TIE statement, and everything works fine.

Are there any gotchas with open(my $f, '<:encoding(UTF-8)', $n)

I am having a problem that I am unable to reproduce in a manner suitable for Stackoverflow although it's reproducable in my production environment.
The problem occors in a Perl script that, among others, iterates over a file that looks like so:
abc-4-9|free text, possibly containing non-ascii characters|
cde-3-8|hällo wörld|
# comment
xyz-9-1|and so on|
qrs-2-8|and so forth|
I can verify the correctness of the file with this Perl script:
use warnings;
use strict;
open (my $f, '<:encoding(UTF-8)', 'c:\path\to\file') or die "$!";
while (my $s = <$f>) {
chomp($s);
next unless $s;
next if $s =~ m/^#/;
$s =~ m!(\w+)-(\d+)-(\d+)\|([^|]*)\|! or die "\n>$s<\n didn't match on line $.";
}
print "Ok\n";
close $f;
When I run this script, it won't die on line 10 and consequently print Ok.
Now, I use essentially the same construct in a huge Perl script (hence irreproducable for Stackoverflow) and it will die on line 2199 of the input file.
If I change the first line (which is completely unrelated to line 2199) from something like
www-1-1|A line with some words|
to
www-1-1|x|
the script will process line 2199 (but fail later).
Interestingly, this behaviour was introduced when I changed
open (my $f, '<', 'c:\path\to\file') or die "$!";
to
open (my $f, '<:encoding(UTF-8)', 'c:\path\to\file') or die "$!";
Without the :encoding(UTF-8) directive, the script does not fail. Of course, I need the encoding directive since the file contains non-ascii characters.
BTW, the same script runs without problems on Linux.
On Windows, where it fails, I use Strawberry Perl 5.24
I do not have a full and correct explanation of why this is necessary, but you can try opening the file with
'<:unix:encoding(UTF-8)'
This may be related to my question "Why is CRLF set for the unix layer on Windows?" which I noticed when I was trying to figure out stuff which I ended up never figuring out.

unable to generate error and redirect it to a file in perl

I am trying to redirect my STDOUT and STDERR to some file. I am successful with that to some extent. But i am not able to understand one thing in the below code.
#!/usr/bin/perl
open (STDOUT,">/var/tmp/outfile") or die "problem : $!";
open (STDERR,">>/var/tmp/outfile") or die "problem : $!";
print "$_\n" foreach (1..10);
sdsdf; # buggy line inserted wantedly
I have inserted the last line assuming that perl would throwout an error and that would be redirected to the file but its not happening . My program does not throughout any error onto the screen nor to the outfile. Please help me understand this behavior.
The sdsdf is not generating any errors (if you use strict then you'll see some compile time errors), that's why you are not seeing any messages. Try this:
use warnings;
use strict;
open (STDOUT,">outfile1") or die "problem : $!";
open STDERR, ">&STDOUT";
print "$_\n" foreach (1..10);
die("aaaa"); # buggy line inserted wantedly
Also in your code you are opening the same file twice, this might cause some problems. In the above we first redirect the stdout to a file then redirect stderr to stdout.
Without use strict;,
sdsdf;
is the same as
"sdsdf";
That's one of the reasons you always want to use use strict; use warnings;. Let's start by adding that.
So you want to log all output including compile-time errors to a file. Well, that's not going to happen by redirecting STDERR after your code has been compiled. The best way to do this is from outside your program.
script.pl >/var/tmp/outfile 2>&1
but it can be done from within your program.
#!/usr/bin/perl
use strict;
use warnings;
BEGIN {
open(STDOUT, '>', '/var/tmp/outfile')
or die("Can't redirect STDOUT: $!\n");
open(STDERR, '>&', \*STDOUT)
or die("Can't redirect STDERR: $!\n");
}
print "$_\n" foreach (1..10);
sdsdf; # Syntax error

perl unable to copy contents of file and print it

I need to read/copy the contents of a file(test.pl) just as the way it is formatted and email it.
I am using the following code but I am unable to print anything out.
I get this error even though the file exists in the same directory.
Failed: No such file or directory
Code:
#!/usr/bin/perl
use strict;
use warnings;
use DBI;
open my $fh, '<', 'test.pl '
or die "Failed: $!\n";
my $text = do {
local $/;
<$fh>
};
close $fh
or die "Failed again: $!\n";
print $text, "\n";
It looks like there is an extra space in the filename you are trying to open. In your open statement, try changing 'test.pl ' to 'test.pl'.
if you are going to read files names from STDIN (user's input), you may want to trim them either by using regex (s/^\s+//....) or Text::Trim among other validations.