File handles not working properly in Perl - perl

I tried initializing two file handles to NULL and later use it in my program.
This was my code:
my $fh1 = " ";
my $fh2 = " ";
open($fh1, ">", $fname1);
open($fh2, ">", $fname2);
print $fh1 "HI this is fh1";
After executing, my files contained this:
fname1 is empty
fname2 cointains
Hi this is fh1
What was the mistake ?
Why is fname1 empty while fname2 contains a string, even though I haven't inserted any string in fh2?

You have set $fh1 and $fh2 to the same value (a space character, not NULL) and so they refer to the same underlying typeglob for I/O.
Filehandles in Perl are a special variable type called a glob or typeglob. In the old days of Perl 4, you always referred to a glob as a character string, often as a bareword. The barewords STDIN, STDOUT, and STDERR are relics of this simpler time.
Nowadays, you can (and usually should) use lexical filehandles, but the underlying reference to a typeglob will still be there. For example, you can write
my $fh = 'STDOUT';
print $fh "hello world\n";
and this will do the exact same thing as
print STDOUT "hello world\n";
Now if you pass an uninitialized scalar as the first argument to open, Perl will assign an arbitrary typeglob to it. You probably don't need to know which typeglob it is.
But if the argument to open is already initialized, Perl uses the typeglob with that argument's value. So this snippet of code will create and add data to a file:
my $fh = "FOO";
open $fh, '>', '/tmp/1';
print FOO "This is going into /tmp/1\n";
close $fh;
Now we can look at your example. You have set $fh1 and $fh2 to the same value -- a string consisting of a space character. So your open call to $fh1 creates an association between a typeglob named " " and the file descriptor for the output stream to $fname1.
When you call open on $fh2, you are reusing the typeglob named " ", which will automatically close the other filehandle using the same typeglob ($fh1), the same was as if you say open FOO, ">/tmp/1"; open FOO, ">/tmp/2", the second open call will implicitly close the first filehandle.
Now you are printing on $fh1, which refers to the typeglob named " ", which is associated with the output stream to file $fname2, and that's where the output goes.
It was a mistake to initialize $fh1 and $fh2. Just leave them undefined:
my ($fh1, $fh2);
open $fh1, ">", ... # assigns $fh1 to arbitrary typeglob
open $fh2, ">", ... # assigns $fh2 to different arbitrary typeglob

You shouldn't initialise your file handles at all, otherwise Perl will try to use that value as a file handle instead of creating a new one. In this case you have opened $fname1 on the file handle ' ' (a single space) and then opened $fname2 on the same file handle, which closes $fname1.
Rather than declaring the file handles separately, it is best to declare them in the open statement, like this
open my $fh1, '>', $fname1;
open my $fh2, '>', $fname2;
then there is less that can go wrong

Related

PERL Net::DNS output to file

Completely new to Perl (in the process of learning) and need some help. Here is some code that I found which prints results to the screen great, but I want it printed to a file. How can I do this? When I open a file and send output to it, I get garbage data.
Here is the code:
use Net::DNS;
my $res = Net::DNS::Resolver->new;
$res->nameservers("ns.example.com");
my #zone = $res->axfr("example.com");
foreach $rr (#zone) {
$rr->print;
}
When I add:
open(my $fh, '>', $filename) or die "Could not open file '$filename' $!";
.....
$rr -> $fh; #I get garbage.
Your #zone array contains a list of Net::DNS::RR objects, whose print method stringifies the object and prints it to the currently selected file handle
To print the same thing to a different file handle you will have to stringify the object yourself
This should work
open my $fh, '>', $filename or die "Could not open file '$filename': $!";
print $fh $_->string, "\n" for #zone;
When you're learning a new language, making random changes to code in the hope that they will do what you want is not a good idea. A far better approach is to read the documentation for the libraries and functions that you are using.
The original code uses $rr->print. The documentation for Net::DNS::Resolver says:
print
$resolver->print;
Prints the resolver state on the standard output.
The print() method there is named after the standard Perl print function which we can use to print data to any filehandle. There's a Net::DNS::Resolver method called string which is documented like this:
string
print $resolver->string;
Returns a string representation of the resolver state.
So it looks like $rr->print is equivalent to print $rr->string. And it's simple enough to change that to print to your new filehandle.
print $fh $rr->string;
p.s. And, by the way, it's "Perl", not "PERL".

Using Perl FileHandle with a scalar containing string instead of filename

My script download a plain text file using from the internet using LWP::Simple's get() function.
I'd the like to process this string in a filehandle way. I found this 'elegant' (well, I like it) way of doing this from http://www.perlmonks.org/?node_id=745018 .
my $filelike = get($url); # whole text file sucked up in single string
open my $fh, '<', \$filelike or die $!;
while (<$fh>) {
# do wildly exciting stuff;
};
But I like using FileHandle; however, I've not found a way of doing the above using it. So:
my $filelike = get($url);
my $fh = new FileHandle \$filelike; # does not work
my $fh = new FileHandle $filelike; # does not work either
Any ideas?
Thanks.
FileHandle provides an fdopen method which can give you a FileHandle object from a symbol reference. You can open a raw filehandle to the scalar ref and then wrap that in a FileHandle object.
open my $string_fh, '<', \$filelike;
my $fh = FileHandle->new->fdopen( $string_fh, 'r' );
(Also, see this answer for why you should use Class->new instead of the indirect new Class notation.)
Do you realize that all file handles are objects of the IO::Handle? If all you want is to use the file handle as an object, you don't have to do anything at all.
$ perl -e'
open my $fh, "<", \"abcdef\n";
STDOUT->print($fh->getline());
'
abcdef
Note: In older versions of Perl, you will need to add use IO::Handle;.

Opening files in-memory means what?

You can open file handles in-memory ?
The in-memory part is unclear to me, what does that mean ?
If that means you can use the computer's memory, Isn't it already working like that ?
It means you can use filehandles to write to and read from scalar variables.
my $var = "";
open my $fh, '>', \$var;
print $fh "asdf";
close $fh;
print $var; # asdf
Ultimately, this is just One More Way To Do
$var .= "asdf"
but there are contexts where is more convenient or more appropriate to use filehandle paradigms than string manipulation paradigms.
For example, start with this code:
open my $fh, '>', $logfile;
...
print $fh $some_message_to_be_logged;
... 500 more print $fh statements ...
close $fh;
But you know what? Now I'd rather record my log messages in a scalar variable, maybe so I can search through them, manipulate them before they are written to disk, etc. I could change all my print statements to
$logvar .= $some_message_to_be_logged
but in this case it is more convenient to just change the open statement.
open my $fh, '>', \$logvar
You can open Filehandles directly to scalar variables. Its especially useful when you have something that has to behave like a file, but you dont want one on disk. This example is taken from perldoc:
close STDOUT;
open(STDOUT, ">", \$variable)
or die "Can't open STDOUT: $!";
It closes STDOUT and then reopens it attached to $variable.

Why isn't $ARGV[0] initialized by the file name I pass this perl one liner?

I have a perl one liner that supposed to export each individual line in an xml file to it's own separate file with the name of the original file and then the line number within that file that it came from.
For instance, if the xml file is called "foo.xml" and it has 100 lines in it then I want to have a hundred files called, "foo_1.xml", "foo_2.xml", "foo_3.xml", etc.
I thought that the name of the file that I pass to the one liner would be available via ARGV, but it's not. I'm getting a "uninitialized value in $ARGV[0]" error when I run this:
perl -nwe 'open (my $FH, ">", "$ARGV[0]_$.\.xml"); print $FH $_;close $FH;' foo.xml
What am I overlooking?
When using the magic <> filehandle (which you're doing implicitly with the -n option), Perl shifts the filenames out of #ARGV as it opens them. (This is mentioned in perlop.) You need to use the plain scalar $ARGV, which contains the filename currently being read from:
perl -nwe 'open (my $FH, ">", "${ARGV}_$.\.xml"); print $FH $_;close $FH;' foo.xml
(The braces are necessary because $ARGV_ is a legal name for a variable.)
cjm has the correct answer. However, it will create files such as foo.xml_1.xml. You asked for foo_1.xml, etc.
perl -nwe '
my ($file) = $ARGV =~ /(\w+)/;
open my $fh, ">", $file . "_$..xml" or die $!;
print $fh $_;
' foo.xml

Perl open file problem

I am having some trouble trying to print from a file. Any ideas? Thanks
open(STDOUT,">/home/int420_101a05/shttpd/htdocs/receipt.html");
#Results of a sub-routine
&printReceipt;
close(STDOUT);
open(INF,"/home/int420_101a05/shttpd/htdocs/receipt.html"); $emailBody = <INF>;
close(INF);
print $emailBody;
ERRORS: Filehandle STDOUT reopened as INF only for input at ./test.c line 6.
print() on closed filehandle STDOUT at ./test.c line 9.
This discussion addresses the technical reason for the message. Relevant info from the thread is this:
From open(2) manpage:
When the call is successful, the file descriptor returned will be
the lowest file descriptor not currently open for the process.
But STDOUT still refers to the
filehandle #1. This warning could be
useful. Although one can argue that
further uses of STDOUT as an output
filehandle will trigger a warning as
well...
So, to summarize, you closed STDOUT (file descriptor 1) and your file will be open as FD#1. That's due to open()'s properties.
As other have noted, the real reason you're having this problem is that you should not use STDOUT for printing to a file unless there's some special case where it's required.
Instead, open a file for writing using a new file handle:
open(OUTFILE,">/home/int420_101a05/shttpd/htdocs/receipt.html")
|| die "Could not open: $!";
print OUTFILE "data";
close(OUTFILE);
To print to filehandle from subroutine, just pass the file handle as a parameter.
The best way of doing so is to create an IO::File object and pass that object around
my $filehandle = IO::File->new(">$filename") || die "error: $!";
mySub($filehandle);
sub mySub {
my $fh = shift;
print $fh "stuff" || die "could not print $!";
}
You can also set a particular filehandle as a default filehandle to have print print to that by default using select but that is a LOT more fragile and should be avoidded in favor of IO::File solution.
If you want to temporarily change the standard output, use the select builtin. Another option is to localize the typeglob first:
{
local *STDOUT;
open STDOUT, '>', 'outfile.txt' or die $!;
print "Sent to file\n";
}
Don't try to open the STDOUT handle. If you want to print to STDOUT, just use print (with no filehandle argument). If you want to print to something other than STDOUT, use a different name.