How to derefence a copy of a STDIN filehandle? - perl

I'm trying to figure out how to get a Perl module to deference and open a reference to a filehandle. You'll understand what I mean when you see the main program:
#!/usr/bin/perl
use strict;
use warnings;
use lib '/usr/local/share/custom_pm';
use Read_FQ;
# open the STDIN filehandle and make a copy of it for (safe handling)
open(FILECOPY, "<&STDIN") or die "Couldn't duplicate STDIN: $!";
# filehandle ref
my $FH_ref = \*FILECOPY;
# pass a reference of the filehandle copy to the module's subroutine
# the value the perl module returns gets stored in $value
my $value = {Read_FQ::read_fq($FH_ref)};
# do something with $value
Basically, I want the main program to receive input via STDIN, make a copy of the STDIN filehandle (for safe handling) then pass a reference to that copy to the read_fq() subroutine in the Read_FQ.pm file (the perl module). The subroutine will then read the input from that file handle, process it, and return a value. Here the Read_FQ.pm file:
package Read_FQ;
sub read_fq{
my ($filehandle) = #_;
my contents = '';
open my $fh, '<', $filehandle or die "Too bad! Couldn't open $filehandle for read\n";
while (<$fh>) {
# do something
}
close $fh;
return $contents;
Here's where I'm running into trouble. In the terminal, when I pass a filename to the main program to open:
cat file.txt | ./script01.pl
it gives the following error message: Too bad! Couldn't open GLOB(0xfa97f0) for read
This tells me that the problem is how I'm dereferencing and opening the reference to the filehandle in the perl module. The main program is okay. I read that $refGlob = \*FILE; is a reference to a file handle and in most cases, should automatically be dereferenced by Perl. However, that isn't that case here. Does anyone know how to dereference a filehandle ref so that I can process it?
thanks. Any suggestions are greatly appreciated.

Your $filehandle should already be open - you had opened FILECOPY, taken a reference and put it in $FH_ref, which is $filehandle. If you want to re-open it again use the <& argument in open or just start reading from it right away.

If I understand correctly, you want the 3-arg equivalent of
open my $fh, '<&STDIN'
That would be
open my $fh, '<&', $filehandle

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

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

Perl: Opening File

I am trying to open the file received as argument.
When i store the argument in to the global variable open works successfully.
But
If I use give make it as my open fails to open the file.
What is the reason.
#use strict;
use warnings;
#my $FILE=$ARGV[0]; #open Fails to open the file $FILE
$FILE=$ARGV[0]; #Works Fine with Global $FILE
open(FILE)
or
die "\n ". "Cannot Open the file specified :ERROR: $!". "\n";
Unary open works only on package (global) variables. This is documented on the manpage.
A better way to open a file for reading would be:
my $filename = $ARGV[0]; # store the 1st argument into the variable
open my $fh, '<', $filename or die $!; # open the file using lexically scoped filehandle
print <$fh>; # print file contents
P.S. always use strict and warnings while debugging your Perl scripts.
It's all in perldoc -f open:
If EXPR is omitted, the scalar variable of the same name as
the FILEHANDLE contains the filename. (Note that lexical
variables--those declared with "my"--will not work for this
purpose; so if you're using "my", specify EXPR in your call
to open.)
Note that this isn't a very good way to specify the file name. As you can see, it has a hard constraint on the variable type it's in, and either the global variable it requires or the global filehandle it opens are usually best avoided.
Using a lexical filehandle keeps its scope in control, and handles closing automatically:
open my $fh, '<', "filename" or die "string involving $!";
And if you're taking that file name from the command line, you could possibly do away with that open or any handle altogether, and use the plain <> operator to read from command-line arguments or STDIN. (see comments for more on this)
use strict;
use warnings;
my $file_name = shift #ARGV;
open(my $file, '<', $file_name) or die $!;
…
close($file);
Always use strict and warnings. If either of them complains, fix the code, do not comment out the pragmas. You can also use autodie to avoid the explicit or die after open, see autodie.
From Perl's docs for open()
If EXPR is omitted, the scalar variable of the same name as the FILEHANDLE contains the filename. (Note that lexical variables--those declared with my--will not work for this purpose; so if you're using my, specify EXPR in your call to open.)

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.

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.