how to check that file was opened successfully in two lines - perl

Sorry, but I can't find answer to this.
If I have the following line
my $FH;
open FH,"somefile";
Now I want to check if FH was opened successfully, but I would like to do this in different line, not using the or syntax.
I tried if ( $FH ), but it did not work for me.
thanks.

From the open-Manual:
Open returns nonzero on success, the undefined value otherwise. If the
open involved a pipe, the return value happens to be the pid of the
subprocess.
So you could do something like that:
my $FH;
my $file_opened = open $FH, '<', 'somefile';
if($file_opened) {
print "open";
close($FH);
}
Or without the additional variable in one line:
if(open $FH, '<', 'somefile') {
print "--open";
close($FH);
}

You can do it in one line:
open( my $FH, '<', 'somefile' ) or die "Could not open file: $!\n";
This gives you the advantage of printing out the reason that the file could not be opened.
Edit: If you want to do the same thing in two lines:
use autodie;
open( my $FH, '<', 'somefile' );

If you really don't want to use the return value of open, the core module Scalar::Util provides the openhandle function that will tell you if a variable is a file handle that is ready to use.
use Scalar::Util 'openhandle';
open my $FH, '<', 'some_file.txt';
unless (openhandle $FH) {
# handle error here
}

Related

How to make an array containing strings in a file separated by space?

I have a file perl_script_2_out_2.txt and I want to put all strings separated by space in an array #arr.
I wrote this code but isnt working .
open my $FILE4, '<', 'perl_script_2_out_2.txt' or die $!;
my #array4 = <FILE4>;
close($FILE4);
open my $FILE5, '>', 'perl_script_2_out_2.txt' or die $!;
foreach $_ (#array4) {
s/\s+/\n/g;
print $FILE5 "$_";
}
close($FILE5);
open my $FILE6, '<', 'perl_script_2_out_2.txt' or die $!;
#arr = <$FILE6>;
You must always use strict and use warnings at the top of every Perl program that you write. In this case you would have seen the message
Name "main::FILE4" used only once: possible typo
which points to the statement
my #array4 = <FILE4>
and helps you to see that you have opened the file handle $FILE4 but tried to read from FILE4, which is different.
If you fix that then your code will work, but it's a strange way to do things and it's much better like this. I have used Data::Dump to display the final contents of the array; it's not necessary for the program to work.
use strict;
use warnings;
open my $fh, '<', 'perl_script_2_out_2.txt' or die $!;
my #arr;
while (<$fh>) {
push #arr, split;
}
use Data::Dump;
dd \#arr;
output
[
"uart_1_baddress",
2211,
"uart_2_baddress",
3344,
"uart_3_baddress",
2572,
]

Read and Write to a file in perl

this
is just
an example.
Lets assume the above is out.txt. I want to read out.txt and write onto the same file.
<Hi >
<this>
<is just>
<an example.>
Modified out.txt.
I want to add tags in the beginning and end of some lines.
As I will be reading the file several times I cannot keep writing it onto a different file each time.
EDIT 1
I tried using "+<" but its giving an output like this :
Hi
this
is just
an example.
<Hi >
<this>
<is just>
<an example.>
**out.txt**
EDIT 2
Code for reference :
open(my $fh, "+<", "out.txt");# or die "cannot open < C:\Users\daanishs\workspace\CCoverage\out.txt: $!";
while(<$fh>)
{
$s1 = "<";
$s2 = $_;
$s3 = ">";
$str = $s1 . $s2 . $s3;
print $fh "$str";
}
The very idea of what you are trying to do is flawed. The file starts as
H i / t h i s / ...
If you were to change it in place, it would look as follows after processing the first line:
< H i > / i s / ...
Notice how you clobbered "th"? You need to make a copy of the file, modify the copy, the replace the original with the copy.
The simplest way is to make this copy in memory.
my $file;
{ # Read the file
open(my $fh, '<', $qfn)
or die "Can't open \"$qfn\": $!\n";
local $/;
$file = <$fh>;
}
# Change the file
$file =~ s/^(.*)\n/<$1>\n/mg;
{ # Save the changes
open(my $fh, '>', $qfn)
or die "Can't create \"$qfn\": $!\n";
print($fh $file);
}
If you wanted to use the disk instead:
rename($qfn, "$qfn.old")
or die "Can't rename \"$qfn\": $!\n";
open(my $fh_in, '<', "$qfn.old")
or die "Can't open \"$qfn\": $!\n";
open(my $fh_out, '>', $qfn)
or die "Can't create \"$qfn\": $!\n";
while (<$fh_in>) {
chomp;
$_ = "<$_>";
print($fh_out "$_\n");
}
unlink("$qfn.old");
Using a trick, the above can be simplified to
local #ARGV = $qfn;
local $^I = '';
while (<>) {
chomp;
$_ = "<$_>";
print(ARGV "$_\n");
}
Or as a one-liner:
perl -i -pe'$_ = "<$_>"' file
Read contents in memory and then prepare required string as you write to your file. (SEEK_SET to zero't byte is required.
#!/usr/bin/perl
open(INFILE, "+<in.txt");
#a=<INFILE>;
seek INFILE, 0, SEEK_SET ;
foreach $i(#a)
{
chomp $i;
print INFILE "<".$i.">"."\n";
}
If you are worried about amount of data being read in memory, you will have to create a temporary result file and finally copy the result file to original file.
You could use Tie::File for easy random access to the lines in your file:
use Tie::File;
use strict;
use warnings;
my $filename = "out.txt";
my #array;
tie #array, 'Tie::File', $filename or die "can't tie file \"$filename\": $!";
for my $line (#array) {
$line = "<$line>";
# or $line =~ s/^(.*)$/<$1>/g; # -- whatever modifications you need to do
}
untie #array;
Disclaimer: Of course, this option is only viable if the file is not shared with other processes. Otherwise you could use flock to prevent shared access while you modify the file.
Disclaimer-2 (thanks to ikegami): Don't use this solution if you have to edit big files and are concerned about performance. Most of the performance loss is mitigated for small files (less than 2MB, though this is configurable using the memory arg).
One option is to open the file twice: Open it once read-only, read the data, close it, process it, open it again read-write (no append), write the data, and close it. This is good practice because it minimizes the time you have the file open, in case someone else needs it.
If you only want to open it once, then you can use the +< file type - just use the seek call between reading and writing to return to the beginning of the file. Otherwise, you finish reading, are at the end of the file, and start writing there, which is why you get the behavior you're seeing.
Need to specify
use Fcntl qw(SEEK_SET);
in order to use
seek INFILE, 0, SEEK_SET;
Thanks user1703205 for the example.

Perl: if a subroutine from a package prints something to my terminal, how do I use its output in my program without changing the package?

I am using a package in Perl (Biomart) that prints out the results of a query. The syntax that prints the output looks like this:
$query_runner->execute($query);
$query_runner->printResults();
And that prints the results of my query to my terminal. Instead, I would like the stuff that's printed to be printed to an output file. I tried:
$output = "#ARGV[1]";
open OUT , ">$output" or die "Can't open $output: #ARGV[1].txt!\n";
$query_runner->execute($query);
print OUT $query_runner->printResults();
But that does not seem to work, the subroutine printResults() still prints to my terminal instead of the output file. Is there a way to print its output to my outputfile without changing the subroutine of the package itself?
You can use select to set the default print filehandle, eg.
select (OUT);
You can reopen STDOUT to write to the given file, call the printing sub and then restore the old STDOUT:
open my $oldout, ">&STDOUT" or die "Can't dup STDOUT: $!";
open STDOUT, '>', $ARGV[1] or die "Can't open $ARGV[1]";
$query_runner->printResults();
open STDOUT, ">&", $oldout or die "Can't dup \$oldout: $!";
From https://github.com/pubmed2ensembl/biomart-plus-extras/blob/master/lib/BioMart/QueryRunner.pm :
sub printResults {
my ($self, $filehandle, $lines) = #_;
$filehandle ||= \*STDOUT; # in case no fhandle is provided
...
}
Thus, printResults takes an optional argument of a filehandle to output to. If not provided, it defaults to STDOUT. You would use it as:
open(my $output, ">", $ARGV[1]);
$query_runner->execute($query);
$query_runner->printResults($output);

How to print into two files at once?

I'm having trouble getting this line of code to work:
for my $fh (FH1, FH2, FH3) { print $fh "whatever\n" }
I found it at perldoc but it doesn't work for me.
The code I have so far is:
my $archive_dir = '/some/cheesy/dir/';
my ($stat_file,$stat_file2) = ($archive_dir."file1.txt",$archive_dir."file2.txt");
my ($fh1,$fh2);
for my $fh (fh1, fh2) { print $fh "whatever\n"; }
I'm getting "Bareword" errors on the (fh1, fh2) part because I'm using strict. I also noticed they were missing a ; in the example, so I'm guessing there might be some more errors aside from that.
What's the correct syntax for printing to two files at once?
You haven't opened the files.
my ($fh1,$fh2);
open($fh1, ">", $stat_file) or die "Couldn't open $stat_file: $!";
open($fh2, ">", $stat_file2) or die "Couldn't open $stat_file2: $!";
for my $fh ($fh1, $fh2) { print $fh "whatever\n"; }
Notice that I'm not using barewords. In the olden days, you would have used:
open(FH1, ">$stat_file");
...
for my $fh (FH1, FH2) { print $fh "whatever\n"; }
but the modern approach is the former.
I would just use IO::Tee.
use strict;
use warnings;
use autodie; # open will now die on failure
use IO::Tee;
open my $fh1, '>', 'file1';
open FH2, '>', 'file2';
my $both = IO::Tee->new( $fh1, \*FH2 );
print {$both} 'This is file number ';
print {$fh1} 'one';
print FH2 'two';
print {$both} "\n";
print {$both} "foobar\n";
$both->close;
Running the above program results in:
file1
This is file number one
foobar
file2
This is file number two
foobar
I would recommend reading the whole perldoc file for more advanced usage.
That looks about right, it's just that it used to be common in Perl to use barewords as file handles, but nowadays it's recommended to use normal scalars.
So make sure that you actually have the files open, then just substitute the (fh1, fh2) part with the actual file handles (which would be ($fh1, $fh2) or something)
another version based off of Brian's answer:
open(my $fh1, ">", $stat_file) or die "Couldn't open $stat_file!";
open(my $fh2, ">", $stat_file2) or die "Couldn't open $stat_file2!";
for ($fh1, $fh2) { print $_ "whatever\n"; }
You first need to open the file in order to get valid filehandles
open (MYFILEA, $stat_file);
open (MYFILEB, $stat_file2);
for my $fh ( \*MYFILEA, \*MYFILEB ) { print $fh "whatever\n" }
close (MYFILEA);
close (MYFILEB);

Can I find a filename from a filehandle in Perl?

open(my $fh, '>', $path) || die $!;
my_sub($fh);
Can my_sub() somehow extrapolate $path from $fh?
A filehandle might not even be connected to a file but instead to a network socket or a pipe hooked to the standard output of a child process.
If you want to associate handles with paths your code opens, use a hash and the fileno operator, e.g.,
my %fileno2path;
sub myopen {
my($path) = #_;
open my $fh, "<", $path or die "$0: open: $!";
$fileno2path{fileno $fh} = $path;
$fh;
}
sub myclose {
my($fh) = #_;
delete $fileno2path{fileno $fh};
close $fh or warn "$0: close: $!";
}
sub path {
my($fh) = #_;
$fileno2path{fileno $fh};
}
Whoever might be looking for better way to find the file name from filehandle or file descriptor:
I would prefer to use the find -inum , if available.
Or, how about using following way, always - any drawbacks except the unix/linux compatible!
my $filename='/tmp/tmp.txt';
open my $fh, '>', $filename;
my $fd = fileno $fh;
print readlink("/proc/$$/fd/$fd");
You can call stat or IO::Handle::stat on a filehandle -- that will give you the device and inode of the file that you have opened. With that and a little operating system wizardry you can find the filename. OK, maybe a lot of operating system wizardry.
The find command has an -inum option to find a file with a specified inode number. This is probably not going to be as efficient as caching the path when you open the file, as gbacon recommends.