error : Name "main::outFile" used only once - perl

I a problem with my perl script:
use strict;
use warnings;
use autodie;
my $out = "result2.txt";
open outFile, ">$out" or die $!;
my %permitted = do {
open my $fh, '<', 'f1.txt';
map { /(.+?)\s+\(/, 1 } <$fh>;
};
open my $fh, '<', 'f2.txt';
while (<$fh>) {
my ($phrase) = /(.+?)\s+->/;
if ($permitted{$phrase}) { print outFile $phrase ;}
}
close outFile;
The error is :
Name "main::outFile" used only once: possible typo at teeest.pl line 14.
Any idea please?
thank you

print has a very special syntax. Without use autodie,
print outFile $phrase;
means
print *outFile $phrase;
But the print replacement use autodie; creates can't quite reproduce that. It probably ends up being
print "outFile" $phrase;
which still does the right thing, but hides the use of outFile from the "used only once" warning checker.
The warning is spurious and harmless in this case. You can prevent it from being emitted by avoiding the unwarranted use of a global variable.
open my $outFile, ">$out" or die $!;
print $outFile $phrase;
close $outFile;

Related

unable to write the output into a file in perl

I want to write the result of my code into a file , but it's not written into my file. My code is working and it's about removing duplicate line, but when i wanted to write the output into a file, the file is empty. This is my code:
use strict;
use warnings;
open(DATA,"/root/Desktop/SIEMENS/printtokens/outputs/common_distinct/MR1/thiscommon.txt");
open FILE2, ">/root/Desktop/SIEMENS/printtokens/outputs/common_distinct/MR1/common_element.txt" or die $!;
my %lines;
#open DATA, $ARGV[0] or die "Couldn't open $ARGV[0]: $!\n";
while (<DATA>) {
print if not $lines{$_}++;
print FILE2 if not $lines{$_}++;
}
close DATA;
close FILE2;
This works as expected
use strict;
use warnings;
use autodie;
open(my $in, '<', './in.txt');
open(my $out, '>', './out.txt');
my %lines;
while (<$in>) {
print $out $_ unless $lines{$_}++;
}
close $in;
close $out;
Note the usage of the 3-argument open, lexical filehandles and the autodie pragma.
Me as a fan of the usage of CPAN modules, would write the same as:
use 5.014;
use warnings;
use Path::Tiny;
use List::Util qw(uniq);
my $out_file = './out.txt';
my $in_file = './in.txt';
path($out_file)->spew( uniq path($in_file)->lines );
Your main problem probably is that $lines{$_}++ changes the value of $lines{$_} so the second will never evaluate to false. You could probably fix your problem just by removing ++ on the first occurence. But for readability I would recommend wrapping both output lines with one if statement, avoid using $_ and remembering to close the files, so something like:
use strict;
use warnings;
my %lines;
open(my $in, '<', './in.txt');
open(my $out, '>', './out.txt');
while (my $line = <$in>) {
unless ($lines{$line}++) {
print STDOUT $line;
print {$out} $line
}
}
close($in);
close($out);
As you can see, I also prefer lexical filehandles and 3-argument open.
Not quite sure what you want in common_element.txt, but I think the logic in the read loop is wrong. No need to increase the $lines twice, and only output if there is a match from existing data?
Try:
use strict;
use warnings;
open(DATA,"thiscommon.txt");
open (FILE2, '>common_element.txt') or die $!;
my %lines;
while (<DATA>) {
print FILE2 if $lines{$_};
print if not $lines{$_}++;
}
close DATA;
close FILE2;
Only with your paths, obviously.

Can't use an undefined value as a symbol reference on line 12

So I am trying to teach myself perl as a new language. I find the best way to learn a new language is to set myself a project. This project is a text game. I have just started this evening. I need to take input from the user and then write it to a file. As I am going to be doing this over and over again, I thought it would be best to put the code in subroutines, as you can see below.
The only problem is that I keep getting the following error:
Can't use an undefined value as a symbol reference at book1.pl line 12, <> line 2.
Any help would be greatly appreciated :)
#!/usr/bin/perl
use 5.010;
use strict;
use warnings;
my $filename = 'save.txt';
sub open_save{
open(my $fh, '>', $filename) or die "Could not open file '$filename' $!";
}
sub close_save{
close my $fh;
}
print "Welcome to the 40K universe\nWhat is your first name?";
my $first_name = <>;
print"What is your surname?";
my $surname = <>;
my $name = $first_name . $surname;
open_save();
print "$name";
close_save();
my creates and returns a new variable. You pass this new variable to close, which quite legitimately complains that it's not a file handle.
#!/usr/bin/perl
use 5.010;
use strict;
use warnings;
sub open_save {
my ($filename) = #_;
open(my $fh, '>', $filename)
or die "Can't open file '$filename': $!\n";
return $fh;
}
sub close_save {
my ($fh) = #_;
close $fh;
}
{
my $filename = 'save.txt';
...
my $fh = open_save($filename);
print $fh "$name\n";
close_save($fh);
}

redirection of the result in a file text

I do a perl scrip that it creates a hash directly from the contents of the first file, and then reads each line of the second, checks the hash to see if it should be printed.
Here is the perl script :
use strict;
use warnings;
use autodie;
my %permitted = do {
open my $fh, '<', 'f1.txt';
map { /(.+?)\s+\(/, 1 } <$fh>;
};
open my $fh, '<', 'f2.txt';
while (<$fh>) {
my ($phrase) = /(.+?)\s+->/;
print if $permitted{$phrase};
}
I am looking for how i print the result in a file text because this script actually print the result on the screen.
Thank you in advance.
Cordially
$ perl thescript.pl > result.txt
Will run your script and put the printed output in result.txt
Or, from within the script itself:
use strict;
use warnings;
use autodie;
my %permitted = do {
open my $fh, '<', 'f1.txt';
map { /(.+?)\s+\(/, 1 } <$fh>;
};
# Open result.txt for writing:
open my $out_fh, '>', 'result.txt' or die "open: $!";
open my $fh, '<', 'f2.txt';
while (<$fh>) {
my ($phrase) = /(.+?)\s+->/;
# print output to result.txt
print $out_fh $_ if $permitted{$phrase};
}
Open a new filehandle in write mode, then print to it. See perldoc -f print or http://perldoc.perl.org/functions/print.html for more info
...
open my $fh, '<', 'f2.txt';
open my $out_fh, '>', 'output.txt';
while (<$fh>) {
my ($phrase) = /(.+?)\s+->/;
print $out_fh $_
if $permitted{$phrase};
}
mapping the file contents first produces a list of all of the file's lines. This isn't necessarily a bad thing, unless the file's substantially large. grebneke showed how to direct output to a file, using > result.txt. Given this, and the (possible) map issue, consider just passing both files to the script from the command line, and process them using whiles:
use strict;
use warnings;
my %permitted;
while (<>) {
$permitted{$1} = 1 if /(.+?)\s+\(/;
last if eof;
}
while (<>) {
print if /(.+?)\s+->/ and $permitted{$1};
}
Usage: perl script.pl f1.txt f2.txt > result.txt
Hope this helps!

Writing results in a text file with perl

I have a problem when the script print the whole line of text file in a result text file:
use strict;
use warnings;
use autodie;
my $out = "result2.txt";
open my $outFile, ">$out" or die $!;
my %permitted = do {
open my $fh, '<', 'f1.txt';
map { /(.+?)\s+\(/, 1 } <$fh>;
};
open my $fh, '<', 'f2.txt';
while (<$fh>) {
my ($phrase) = /(.+?)\s+->/;
if ($permitted{$phrase}) {
print $outFile $fh;
}
close $outFile;
The problem is in this line
print $outFile $fh;
Any idea please?
Thank you
print $outFile $fh is printing the value of the file handle $fh to the file handle $outFile. Instead you want to print the entire current line, which is in $_.
There are a couple of other improvements that can be made
You should always use the three-parameter form of open, so the open mode appears on its own as the second paremeter
There is no need to test the success of an open of autodie is in place
If you have a variable that contains the name of the output file, then you really should have ones for the names of the two input files as well
This is how your program should look. I hope it helps.
use strict;
use warnings;
use autodie;
my ($in1, $in2, $out) = qw/ f1.txt f2.txt result2.txt /;
my %permitted = do {
open my $fh, '<', $in1;
map { /(.+?)\s+\(/, 1 } <$fh>;
};
open my $fh, '<', $in2;
open my $outfh, '>', $out;
while (<$fh>) {
my ($phrase) = /(.+?)\s+->/;
if ($permitted{$phrase}) {
print $outfh $_;
}
}
close $outfh;
I think you want print $outfile $phrase here, don't you? The line you currently have is trying to print out a file handle reference ($fh) to a file ($outfile).
Also, just as part of perl best practices, you'll want to use the three argument open for your first open line:
open my $outFile, ">", $out or die $!;
(FWIW, you're already using 3-arg open for your other two calls to open.)
Although Borodin has provided an excellent solution to your question, here's another option where you pass your 'in' files' names to the script on the command line, and let Perl handle the opening and closing of those files:
use strict;
use warnings;
my $file2 = pop;
my %permitted = map { /(.+?)\s+\(/, 1 } <>;
push #ARGV, $file2;
while (<>) {
my ($phrase) = /(.+?)\s+->/;
print if $permitted{$phrase};
}
Usage: perl script.pl inFile1 inFile2 [>outFile]
The last, optional parameter directs output to a file.
The pop command implicitly removes inFile2's name off of #ARGV, and stores it in $file2. Then, inFile1 is read using the <> directive. The file name of inFile2 is then pushed onto #ARGV, and that file is read and a line is printed if $permitted{$phrase} is true.
Running the script without the last, optional parameter will print results (if any) to the screen. Using the last parameter saves output to a file.
Hope this helps!

Read Increment Then Write to a text file in perl

I have this little perl script which opens a txt file, reads the number in it, then overwrites the file with the number incremented by 1. I can open and read from the file, I can write to the file but I"m having issues overwriting. In addition, I'm wondering if there is a way to do this without opening the file twice. Here's my code:
#!/usr/bin/perl
open (FILE, "<", "data.txt") or die "$! error trying to a\
ppend";
undef $/;
$number = <FILE>;
$number = int($number);
$myNumber = $number++;
print $myNumber+'\n';
close(FILE);
open(FILE, ">data.txt") or die "$! error";
print FILE $myNumber;
close(FILE);
Change the line
$myNumber = $number++;
to
$myNumber = $number+1;
That should solve the problem.
Below is how you could do by opening the file just once:
open(FILE, "+<data.txt") or die "$! error";
undef $/;
$number = <FILE>;
$number = int($number);
$myNumber = $number+1;
seek(FILE, 0, 0);
truncate(FILE, tell FILE);
print $myNumber+"\n";
print FILE $myNumber;
close(FILE);
It's good that you used the three-argument form of open the first time. You also needed to do that in your second open. Also, you should use lexical variables, i.e., those which begin with my, in your script--even for your file handles.
You can just increment the variable that holds the number, instead of passing it to a new variable. Also, it's a good idea to use chomp. This things being said, consider the following option:
#!/usr/bin/env perl
use strict;
use warnings;
undef $/;
open my $fhIN, "<", "data.txt" or die "Error trying to open for reading: $!";
chomp( my $number = <$fhIN> );
close $fhIN;
$number++;
open my $fhOUT, ">", "data.txt" or die "Error trying to open for writing: $!";
print $fhOUT $number;
close $fhOUT;
Another option is to use the Module File::Slurp, letting it handle all the I/O operations:
#!/usr/bin/env perl
use strict;
use warnings;
use File::Slurp qw/edit_file/;
edit_file { chomp; $_++ } 'data.txt';
Try this:
#!/usr/bin/perl
use strict;
use warnings;
my $file = "data.txt";
my $number = 0;
my $fh;
if( -e $file ) {
open $fh, "+<", $file or die "Opening '$file' failed, because $!\n";
$number = <$fh>;
seek( $fh, 0, 0 );
} else { # if no data.txt exists - yet
open $fh, ">", $file or die "Creating '$file' failed, because $!\n";
}
$number++;
print "$number\n";
print $fh $number;
close( $fh );
If you're using a bash shell, and you save the code to test.pl, you can test it with:
for i in {1..10}; do ./test.pl; done
Then 'cat data.txt', should show a 10.