I'm a little confused on how to save a file that is an IO::Handle.
Here is what I have
use IO::File;
my $iof = IO::File->new;
# open file
$iof->open($path, "w") || die "$! : $path";
# ensure binary
$iof->binmode;
# output file to disk
print $iof $self->File_Upload;
$iof->close;
File_Upload is the IO::Handle given to me via the CGI module for a file upload, but the output in the file is...
IO::Handle=GLOB(0x20dabec)
Not the binary data of the uploaded PDF.
If I have a file in a file handle how do I save it?
Do I need IO::File if I have an IO::Handle?
Your input is appreciated.
1DMF
Read from the CGI provided file handle using readline:
print $iof readline($self->File_Upload);
The fact that your output contents were 'IO::Handle=GLOB(0x20dabec)' implied that the $self->File_Upload is of type IO::Handle and should be treated as such.
Using readline in a list context pulls all the lines as demonstrated above. Alternatively, you could use the object method ->getlines():
print $iof $self->File_Upload->getlines();
How does one slurp a file?
my $fh = $self->File_Upload();
my $file = do { local $/; <$fh> };
Yes, this works for IO::Handle objects in addition to the usual globs (STDIN), references to globs (from open my $fh, ...) and IO scalars (*STDIN{IO}).
Then to print it,
print($iof $file);
In this particular case, you could simply use
print($iof $self->File_Upload()->getlines());
Related
I need some quick perl help. Here is what I want to so:
1) run my perl script from the command line, and pass in as an argument a data file
2) Search the passed in data file, and look for the first occurrence of a word in a string. Replace the entire line that string resides in, with another line of text.
3) Save the updated file to itself (replace within the file).
So for example, something like this:
./myPerlScript.pl data.txt
with the data file being something like this:
_DATA__
path/to/some/file
path/to/some/other/file
path/to/SUBTSTRING/file #replace entire line if SUBSTRING is found
path/to/file
but the actual data.txt is updated (NOT written to a new file)
Same as in any other language.
use Fcntl qw( SEEK_SET );
my $qfn = $ARGV[0];
open(my $fh, '+<', $qfn)
or die("Can't open \"$qfn\": $!\n");
# Read contents of file into $file.
my $file; { local $/; $file = <$fh>; }
if ($file =~ s/^.*SUBSTR.*/foo/mg) {
seek($fh, 0, SEEK_SET)
or die("seek: $!\n");
truncate($fh, 0)
or die("truncate: $!\n");
print($fh $file)
or die("print: $!\n");
close($fh)
or die("close: $!\n");
}
Another way:
Create a temporary file in the same dir as the original file.
Read from the file and write the modified contents to the new file.
If an error occurs, delete the temporary.
Delete the original.
Rename the temporary.
It's what happens when you do the following when using a sufficiently recent version of Perl:
perl -nle'print /SUBSTR/ ? "foo" : $_' -i file
We can access this functionality through $^I
$^I = '';
while (<>) {
chomp;
say /SUBSTR/ ? "foo" : $_;
}
This approach has two advantages:
There's no data loss on error.
It allows us to read the file line by line (thus saving memory).
And three disadvantages:
Requires enough disk space for both the original and the modified file.
Requires permission to create a new file.
File loses whatever owership and permissions it originally had.
I want to copy lines 7-12 of files, like
this example .vect file,
into another .vect file in the same directory.
I want each line, to be copied twice, and the two copies of each line to be pasted consecutively in the new file.
This is the code I have used so far, and would like to continue using these methods/packages in Perl.
use strict;
use warnings;
use feature qw(say);
# This method works for reading a single file
my $dir = "D:\\Downloads";
my $readfile = $dir ."\\2290-00002.vect";
my $writefile = $dir . "\\file2.vect";
#open a file to read
open(DATA1, "<". $readfile) or die "Can't open '$readfile': $!";;
# Open a file to write
open(DATA2, ">" . $writefile) or die "Can't open '$writefile': $!";;
# Copy data from one file to another.
while ( <DATA1> ) {
print DATA2 $_;
}
close( DATA1 );
close( DATA2 );
What would be a simple way to do this using the same opening and closing file syntax I have used above?
Just modify the print line to
print DATA2 $_, $_ if 7 .. 12;
See Range Operators in "perlop - Perl operators and precedence" for details.
It's worth remembering the
Tie::File
module which maps a file line by line to a Perl array and allows you to manipulate text files using simple array operations. It can be slow when working with large amounts of data, but it is ideal for the majority of applications involving regular text files
Copying a range of lines from one file to another becomes a simple matter of copying an array slice. Remember that the file starts with line one in array element zero, so lines 7 to 12 are at indexes 6...11
This is the Perl code to do what you ask
use strict;
use warnings;
use Tie::File;
chdir 'D:\Downloads' or die $!;
tie my #infile, 'Tie::File', '2290-00002.vect' or die $!;
tie my #outfile, 'Tie::File', 'file2.vect' or die $!;
#outfile = map { $_, $_ } #infile[6..11];
Nothing else is required. Isn't that neat?
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;.
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
I'm having trouble modifying a script that processes files passed as command line arguments, merely for copying those files, to additionally modifying those files. The following perl script worked just fine for copying files:
use strict;
use warnings;
use File::Copy;
foreach $_ (#ARGV) {
my $orig = $_;
(my $copy = $orig) =~ s/\.js$/_extjs4\.js/;
copy($orig, $copy) or die(qq{failed to copy $orig -> $copy});
}
Now that I have files named "*_extjs4.js", I would like to pass those into a script that similarly takes file names from the command line, and further processes the lines within those files. So far I am able get a file handle successfully as the following script and it's output shows:
use strict;
use warnings;
foreach $_ (#ARGV) {
print "$_\n";
open(my $fh, "+>", $_) or die $!;
print $fh;
#while (my $line = <$fh>) {
# print $line;
#}
close $fh;
}
Which outputs (in part):
./filetree_extjs4.js
GLOB(0x1a457de8)
./async_submit_extjs4.js
GLOB(0x1a457de8)
What I really want to do though rather than printing a representation of the file handle, is to work with the contents of the files themselves. A start would be to print the files lines, which I've tried to do with the commented out code above.
But that code has no effect, the files' lines do not get printed. What am I doing wrong? Is there a conflict between the $_ used to process command line arguments, and the one used to process file contents?
It looks like there are a couple of questions here.
What I really want to do though rather than printing a representation of the file handle, is to work with the contents of the files themselves.
The reason why print $fh is returning GLOB(0x1a457de8) is because the scalar $fh is a filehandle and not the contents of the file itself. To access the contents of the file itself, use <$fh>. For example:
while (my $line = <$fh>) {
print $line;
}
# or simply print while <$fh>;
will print the contents of the entire file.
This is documented in pelrdoc perlop:
If what the angle brackets contain is a simple scalar variable (e.g.,
<$foo>), then that variable contains the name of the filehandle to
input from, or its typeglob, or a reference to the same.
But it has already been tried!
I can see that. Try it after changing the open mode to +<.
According to perldoc perlfaq5:
How come when I open a file read-write it wipes it out?
Because you're using something like this, which truncates the file
then gives you read-write access:
open my $fh, '+>', '/path/name'; # WRONG (almost always)
Whoops. You should instead use this, which will fail if the file
doesn't exist:
open my $fh, '+<', '/path/name'; # open for update
Using ">" always clobbers or creates. Using "<" never does either. The
"+" doesn't change this.
It goes without saying that the or die $! after the open is highly recommended.
But take a step back.
There is a more Perlish way to back up the original file and subsequently manipulate it. In fact, it is doable via the command line itself (!) using the -i flag:
$ perl -p -i._extjs4 -e 's/foo/bar/g' *.js
See perldoc perlrun for more details.
I can't fit my needs into the command-line.
If the manipulation is too much for the command-line to handle, the Tie::File module is worth a try.
To read the contents of a filehandle you have to call readline read or place the filehandle in angle brackets <>.
my $line = readline $fh;
my $actually_read = read $fh, $text, $bytes;
my $line = <$fh>; # similar to readline
To print to a filehandle other than STDIN you have to have it as the first argument to print, followed by what you want to print, without a comma between them.
print $fh 'something';
To prevent someone from accidentally adding a comma, I prefer to put the filehandle in a block.
print {$fh} 'something';
You could also select your new handle.
{
my $oldfh = select $fh;
print 'something';
select $oldfh; # reset it back to the previous handle
}
Also your mode argument to open, causes it to clobber the contents of the file. At which point there is nothing left to read.
Try this instead:
open my $fh, '+<', $_ or die;
I'd like to add something to Zaid's excellent suggestion of using a one-liner.
When you are new to perl, and trying some tricky regexes, it can be nice to use a source file for them, as the command line may get rather crowded. I.e.:
The file:
#!/usr/bin/perl
use warnings;
use strict;
s/complicated/regex/g;
While tweaking the regex, use the source file like so:
perl -p script.pl input.js
perl -p script.pl input.js > testfile
perl -p script.pl input.js | less
Note that you don't use the -i flag here while testing. These commands will not change the input files, only print the changes to stdout.
When you're ready to execute the (permanent!) changes, just add the in-place edit -i flag, and if you wish (recommended), supply an extension for backups, e.g. ".bak".
perl -pi.bak script.pl *.js