How to print into two files at once? - perl

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

Related

Recursive search in Perl?

I'm incredibly new to Perl, and never have been a phenomenal programmer. I have some successful BVA routines for controlling microprocessor functions, but never anything embedded, or multi-facted. Anyway, my question today is about a boggle I cannot get over when trying to figure out how to remove duplicate lines of text from a text file I created.
The file could have several of the same lines of txt in it, not sequentially placed, which is problematic as I'm practically comparing the file to itself, line by line. So, if the first and third lines are the same, I'll write the first line to a new file, not the third. But when I compare the third line, I'll write it again since the first line is "forgotten" by my current code. I'm sure there's a simple way to do this, but I have issue making things simple in code. Here's the code:
my $searchString = pseudo variable "ideally an iterative search through the source file";
my $file2 = "/tmp/cutdown.txt";
my $file3 = "/tmp/output.txt";
my $count = "0";
open (FILE, $file2) || die "Can't open cutdown.txt \n";
open (FILE2, ">$file3") || die "Can't open output.txt \n";
while (<FILE>) {
print "$_";
print "$searchString\n";
if (($_ =~ /$searchString/) and ($count == "0")) {
++ $count;
print FILE2 $_;
} else {
print "This isn't working\n";
}
}
close (FILE);
close (FILE2);
Excuse the way filehandles and scalars do not match. It is a work in progress... :)
The secret of checking for uniqueness, is to store the lines you have seen in a hash and only print lines that don't exist in the hash.
Updating your code slightly to use more modern practices (three-arg open(), lexical filehandles) we get this:
my $file2 = "/tmp/cutdown.txt";
my $file3 = "/tmp/output.txt";
open my $in_fh, '<', $file2 or die "Can't open cutdown.txt: $!\n";
open my $out_fh, '>', $file3 or die "Can't open output.txt: $!\n";
my %seen;
while (<$in_fh>) {
print $out_fh unless $seen{$_}++;
}
But I would write this as a Unix filter. Read from STDIN and write to STDOUT. That way, your program is more flexible. The whole code becomes:
#!/usr/bin/perl
use strict;
use warnings;
my %seen;
while (<>) {
print unless $seen{$_}++;
}
Assuming this is in a file called my_filter, you would call it as:
$ ./my_filter < /tmp/cutdown.txt > /tmp/output.txt
Update: But this doesn't use your $searchString variable. It's not clear to me what that's for.
If your file is not very large, you can store each line readed from the input file as a key in a hash variable. And then, print the hash keys (ordered). Something like that:
my %lines = ();
my $order = 1;
open my $fhi, "<", $file2 or die "Cannot open file: $!";
while( my $line = <$fhi> ) {
$lines {$line} = $order++;
}
close $fhi;
open my $fho, ">", $file3 or die "Cannot open file: $!";
#Sort the keys, only if needed
my #ordered_lines = sort { $lines{$a} <=> $lines{$b} } keys(%lines);
for my $key( #ordered_lines ) {
print $fho $key;
}
close $fho;
You need two things to do that:
a hash to keep track of all the lines you have seen
a loop reading the input file
This is a simple implementation, called with an input filename and an output filename.
use strict;
use warnings;
open my $fh_in, '<', $ARGV[0] or die "Could not open file '$ARGV[0]': $!";
open my $fh_out, '<', $ARGV[1] or die "Could not open file '$ARGV[1]': $!";
my %seen;
while (my $line = <$fh_in>) {
# check if we have already seen this line
if (not $seen{$line}) {
print $fh_out $line;
}
# remember this line
$seen{$line}++;
}
To test it, I've included it with the DATA handle as well.
use strict;
use warnings;
my %seen;
while (my $line = <DATA>) {
# check if we have already seen this line
if (not $seen{$line}) {
print $line;
}
# remember this line
$seen{$line}++;
}
__DATA__
foo
bar
asdf
foo
foo
asdfg
hello world
This will print
foo
bar
asdf
asdfg
hello world
Keep in mind that the memory consumption will grow with the file size. It should be fine as long as the text file is smaller than your RAM. Perl's hash memory consumption grows a faster than linear, but your data structure is very flat.

what is wrong with the syntax that does not give me an outfile?

I dont know what exactly is wrong but everytime I execute this script i keep getting "No such file or directory at ./reprioritize line 35, line 1".
here is my script that is having an issue:
my $newresult = "home/user/newresults_percengtage_vs_pn";
sub pushval
{
my #fields = #_;
open OUTFILE, ">$newresult/fixedhomdata_030716-031316.csv" or die $!; #line 35
while(<OUTFILE>)
{
if($fields[5] >= 13)
{
print OUTFILE "$fields[0]", "$fields[1]","$fields[2]","$fields[3]","$fields[4]","$fields[5]", "0";
}
elsif($fields[5] < 13 && $fields[5] > 1)
{
print OUTFILE "$fields[0]", "$fields[1]","$fields[2]","$fields[3]","$fields[4]","$fields[5]", "1";
}
elsif($fields[5] <= 1)
{
print OUTFILE "$fields[0]", "$fields[1]","$fields[2]","$fields[3]","$fields[4]","$fields[5]", "2";
}
}
close (OUTFILE);
You may want to have a look at Perl's tutorial on opening files.
I simplify it a bit. There are basically three modes: open for reading, open for writing, and open for appending.
Reading
Opening for reading is indicated by either a < preceeding the filename or on its own, as a separate parameter to the open() call (preferred), i.e.:
my $fh = undef;
my $filename = 'fixedhomdata_030716-031316.csv';
open($fh, "<$filename") or die $!; # bad
open($fh, '<', $filename) or die $!; # good
while( my $line = <$fh> ) { # read one line from filehandle $fh
...
}
close($fh);
When you open the file this way, it must exist, else you get your error (No such file or directory at ...).
Writing
Opening for writing is indicated by a >, i.e.:
open($fh, ">$filename") or die $!; # bad
open($fh, '>', $filename) or die $!; # good
print $fh "some text\n"; # write to filehandle $fh
print $fh "more text\n"; # write to filehandle $fh
...
close($fh);
When you open the file this way, it is truncated (cleared) and overwritten if it existed. If it did not exist, it will get created.
Appending
Opening for appending is indicated by a >>, i.e.:
open($fh, ">>$filename") or die $!; # bad
open($fh, '>>', $filename) or die $!; # good
print $fh "some text\n"; # append to filehandle $fh
print $fh "more text\n"; # append to filehandle $fh
...
close($fh);
When you open the file this way and it existed, then the new lines will be appended to the file, i.e. nothing is lost. If the file did not
exist, it will be created (as if only > had been given).
Your error message doesn't match your code. You opened the file for writing (>) but got doesn't exist, which indicates that you actually opened it for reading.
This might have happened because you use OUTPUT as a filehandle instead of a scoped variable, e.g. $fh. OUTPUT is a global filehandle, i.e. if you open a file this way, then all of your code (no matter which function in) can use OUTPUT. Don't do that. From the docs:
An older style is to use a bareword as the filehandle, as
open(FH, "<", "input.txt")
or die "cannot open < input.txt: $!";
Then you can use FH as the filehandle, in close FH and and so on.
Note that it's a global variable, so this form is not recommended
in new code.
To summarize:
use scoped variables as filehandles ($fh instead of OUTPUT)
open your file in the right mode (> vs. <)
always use three-argument open (open($fh, $mode, $filename) vs. open($fh, "$mode$filename")
The comments explain that your two issues with the snippet are
The missing leading '/' in the $newresult declaration
You are treating your filehandle as both a read and a write.
The first is easy to fix. The second is not as easy to fix properly with knowing the rest of the script. I am making an assumption that pushval is called once per record in a Array of Arrays(?). This snippet below should get the result you want, but there is likely a better way of doing it.
my $newresult = "/home/user/newresults_percengtage_vs_pn";
sub pushval{
my #fields = #_;
open OUTFILE, ">>$newresult/fixedhomdata_030716-031316.csv" or die $!; #line 35
print OUTFILE "$fields[0]", "$fields[1]","$fields[2]","$fields[3]","$fields[4]","$fields[5]"
if($fields[5] >= 13) {
print OUTFILE "0\n";
} elsif($fields[5] < 13 && $fields[5] > 1) {
print OUTFILE "1\n";
} elsif($fields[5] <= 1) {
print OUTFILE "2\n";
}
close (OUTFILE);

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,
]

Cannot read from a file opened in read/write/append mode

Here is my code
use strict;
use warnings;
open(INFILE,"<File1") or die "Can't open the file";
open(OUTFILE,"+>>File2") or die "Can't open the file";
while(<INFILE>) {
print OUTFILE "$_";
}
while(<OUTFILE>) {
print "$_\n";
}
Am trying to move the contents of File1 to File2 which is created in read,write and append mode. The problem here is I have my File2 created with File1 contents, but the print statement inside the while(<OUTFILE>) {} is not printing the contents of File2.
Anyone help me understand what is wrong here!(I might be making a silly mistake)
After your first while loop, you are at the end of the OUTFILE. So, the second while loop is never really executed. Use seek before you read OUTFILE:
use strict;
use warnings;
open(INFILE,"<File1")or die "Can't open the file";
open(OUTFILE,"+>>File2")or die "Can't open the file";
while(<INFILE>)
{
print OUTFILE "$_";
}
seek OUTFILE, 0, 0;
while(<OUTFILE>)
{
print "$_\n";
}
Set the cursor back to the start of OUTFILE:
seek OUTFILE, 0, 0;

how to check that file was opened successfully in two lines

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
}