Perl : Cannot open a file with filename passed as argument - perl

I am passing two filenames from a DOS batch file to a Perl script.
my $InputFileName = $ARGV[0];
my $OutputFileName = $ARGV[1];
Only the input file physically exists while the Outputfile must be created by the script.
open HANDLE, $OutputFileName or die $!;
open (HANDLE, ">$OutputFileName);
open HANDLE, ">$OutputFileName" or die $!;
All three fail.
However the following works fine.
open HANDLE, ">FileName.Txt" or die $!;
What is the correct syntax?
Edit : Error message is : No such file or directory at Batchfile.pl at line nn

The proper way is to use the three-parameter form of open (with the mode as a separate parameter) with lexical file handles. Also die doesn't have a capital D.
Like this
open my $out, '>', $OutputFileName or die $!;
but your last example should work assuming you have spelled die properly in your actual code.
If you are providing a path to the filename that doesn't exist then you also need to create the intermediate directories.
The die string will tell you the exact problem. What message do you get when this fails?

code:
$file_name = $ARGV[1];
open (OUTPUT "> $file_name") or error("unable to create or open $file_name");
print OUTPUT "hello world";
close(OUTPUT);
command to execute:
perl perl_file.pl data.txt
it will work try

Related

How to read a config file from a command line using perl (automation)

I am new to Perl and need help with this.
I want to read a config file and see if the first 10 lines of the file contents starts with # and have or not have some particular string to do some automation in Perl.
I was able to open the file using:
$obj->cmd("path");
$a = ->obj(cat "/path/.config");
I don't know if we can save this to variable or is there any way I can copy it to the text file and search it.
Also how do I edit this file like adding or removing file content.
Thank you
To open a file for reading use:
open(my $fh, "<", "/path/.config") or die "Can't open < .config: $!";
To open a file for writing
open(my $fh, ">", "/path/.config") or die "Can't open > .config: $!";
the content of the file is then assigned to $fh

How to modify content of a file using single file handle

I'm trying to modify content of a file using Perl.
The following script works fine.
#!/usr/bin/perl
use strict;
use warnings;
open(FH,"test.txt") || die "not able to open test.txt $!";
open(FH2,">","test_new.txt")|| die "not able to opne test_new.txt $!";
while(my $line = <FH>)
{
$line =~ s/perl/python/i;
print FH2 $line;
}
close(FH);
close(FH2);
The content of test.txt:
im learning perl
im in File handlers chapter
The output in test_new.txt:
im learning python
im in File handlers chapter
If I try to use same file handle for modifying the content of file, then I'm not getting expected output. The following is the script that attempts to do this:
#!/usr/bin/perl
use strict;
use warnings;
open(FH,"+<","test.txt") || die "not able to open test.txt $!";
while(my $line = <FH>)
{
$line =~ s/perl/python/i;
print FH $line;
}
close(FH);
Incorrect output in test.txt:
im learning perl
im learning python
chapter
chapter
How do I modify the file contents using single file handle?
You can't delete from a file (except at the end).
You can't insert characters into a file (except at the end).
You can replace a character in a file.
You can append to a file.
You can shorten a file.
That's it.
You're imagining you can simply replace "Perl" with "Python" in the file. Those aren't of the same length, so it would require inserting characters into the file, and you can't do that.
You can effectively insert characters into a file by loading the rest of the file into memory and writing it back out two characters further. But doing this gets tricky for very large files. It's also very slow since you end up copying a (possibly very large) portion of the file every time you want to insert characters.
The other problem with in-place modifications is that you can't recover from an error. If something happens, you'll be left with an incomplete or corrupted file.
If the file is small and you're ok with losing the data if something goes wrong, the simplest approach is to load the entire file into memory.
open(my $fh, '<+', $qfn)
or die("Can't open \"$qfn\": $!\n");
my $file = do { local $/; <$fh> };
$file =~ s/Perl/Python/g;
seek($fh, 0, SEEK_SET)
or die $!;
print($fh $file)
or die $!;
truncate($fh)
or die $!;
A safer approach is to write the data to a new file, then rename the file when you're done.
my $new_qfn = $qfn . ".tmp";
open(my $fh_in, '<', $qfn)
or die("Can't open \"$qfn\": $!\n");
open(my $fh_out, '<', $new_qfn)
or die("Can't create \"$new_qfn\": $!\n");
while (<$fh_in>) {
s/Perl/Python/g;
print($fh_out $_);
}
close($fh_in);
close($fh_out);
rename($qfn_new, $qfn)
or die $!;
The downside of this approach is it might change the file's permissions, and hardlinks will point to the old content instead of the new file. You also need permissions to create a file.
As #Сухой27 answered
it's typical situation that perl onliner pleasingly used.
perl -i -pe 's/perl/python/i'
perl takes below options
-p : make line by line loop(every line assign into $_ and print after evaluated $_)
-e : evaluate code block in above loop ( regex take $_ as default operand )
-i : in plcae file edit (if you pass arguments for -i, perl preserve original files with that extention)
if you run below script
perl -i.bak -pe 's/perl/python/i' test.txt
you will get modified test.txt
im learning python
im in File handlers chapter
and get original text files named in test.txt.bak
im learning perl
im in File handlers chapter

In Perl, why can't I open this file using single argument open?

The file is 52MB in size. It is in the same directory as the program.
$big = 'VIXhx.csv';
# tie #optLine, 'Tie::File', $big or die "Cant Tie to $big $!" ;
open $big or die "Cant open $big, $!," ;
Tie::File gave no error message.
Plain open gave the error message:
Cant open VIXhx.csv, No such file or directory, at C:\Python34\hsf\ETFs\VIX\qad.
pl line 47.
(Yes, it's in the Python directory - but Perl works fine there)
I can open the file in the editor, so there doesn't seem to be a problem with the file itself.
I have a smaller file in the same program that opened with no problem in Tie::File.
$dat = 'ETF5Y.csv';
tie #datLine, 'Tie::File', $dat or die "Cant Tie to $dat $!" ;
Is it possible that Perl is unable to open a file if it's too large?
Please check perldoc -f open on how to open files, what you did ended up in opening an empty filename,
strace perl -e '$big = "/etc/passwd"; open $big or die "Cant open $big, $!,"'
output
...
open("", O_RDONLY) = -1 ENOENT (No such file or directory)
write(2, "Cant open /etc/passwd, No such f"..., 64Cant open /etc/passwd, No such file or directory, at -e line 1.
See perldoc perlopentut:
Single Argument Open
Remember how we said that Perl's open took two arguments? That was a passive prevarication. You see, it can also take just one argument. If and only if the variable is a global variable, not a lexical, you can pass open just one argument, the filehandle, and it will get the path from the global scalar variable of the same name.
$FILE = "/etc/motd";
open FILE or die "can't open $FILE: $!";
while (<FILE>) {
# whatever
}
Therefore, if you want single argument open to do what you want, you would have to write our code as
$big = 'VIXhx.csv';
open big or die "Can't open '$big': $!";
# ^ <-- look, no dollar sign before filehandle
Alternatively, you could do something like this:
$big = 'VIXhx.csv';
*{$big} = \$big;
open $big and print <$big>;
if you want to keep the open $big.
But, relying on global variables and effects at a distance is not a good idea. Instead, use the three argument form of open to specify the filehandle, the mode, and the file name separately as in:
open my $vix_fh, '<', $vix_file
or die "Failed to open '$vix_file': $!";
By the way, you won't even find this section on "Single Argument Open" in recent Perl documentation. The following note should give you and idea why:
Why is this here? Someone has to cater to the hysterical porpoises. It's something that's been in Perl since the very beginning, if not before.
The single argument open can also be used to turn any Perl program into a quine.
I found the answer to my original question, of why TIE didn't work.
It turns out that the file used '0A' as the line terminator, so TIE, expecting '0D0A', read the whole 52MB file as one record.
I added recsep => "\n" to the TIE statement, and everything works fine.

How do I drop a file onto a perl script to parse it and write the output file to the same directory

I have a working perl script that opens a imput file, parse it and then open a output file and write the parsed output to it. Now I want to be able to drop a file to be script. The file should be read and the written file should have the same name with a different extension and be stored in the same directory(!) where the file was dropped from. The script itself is converted to a exe using PAR:Packer. I'm using Windows 7 and the latest version Strawbery Perl (5.16.2)
The way I check for the file name is :
unless ($#ARGV == -1) {
$filename = $ARGV[0];
}
This is how I open the input file :
open (my $mel, "<", $filename) or die "\nFile does not exist.\n";
And this is how I open the output file :
open (my $out, ">", 'majorEventLog.Prs') or die "Can't open output file: $!";
The issue I face is that the input file is not recognised at all. Second, what do I need to do to have the output file to be created in the same directory ? When I created a cmd file the input file worked but it stored the output file in my home directory. But I do not like to use the CMD, and I also would like to have the output file in the same directory than the input file. How do I need to change my code?
Use this to get input file name:
my $filename = shift;
die ("Please provide input file\n") unless defined $filename;
die ("$filename does not exist\n") unless -f $filename;
Use this to generate output filename in the same directory:
use File::Basename;
use File::Spec;
my ($name, $directory, $suffix) = fileparse($filename);
my $outfile = File::Spec->catfile($directory, "output.txt");

Why is piping my script's output into the shell's dir command not working as I expected?

I am piping the directory command output to file handle, followed by the print to the file handle. I wish to append some text to the dir/ls output.
open (FH, "| dir") or die "$OS_ERROR";
print FH ("sometext") or die "$OS_ERROR";
while (<FH>){
print;
}
When I execute the Perl script, I see the directory contents, but I do not see the text printed using the print statement, in this case I do not see sometext. What am I missing?
To explain in more detail - I want pipe dir contents to FH, and following that I want to append some text to the same filehandle FH . I have referred link http://perldoc.perl.org/perlopentut.html#Pipe-Opens
You are not redirecting anything: You are piping your script's output to either the cmd.exe builtin dir or an alias to ls depending on your OS (which means, you might run into trouble if you run this script with Cygwin's ls in your path on Windows).
Writing to dir does not seem useful. If you wanted to filter dirs output, i.e. take the output from running dir and manipulate it before printing, you should pipe it into your script and you should print the processed output.
#!/usr/bin/env perl
use strict; use warnings;
my $pid = open my $dir_out, '-|', 'cmd.exe /c dir';
die "Cannot open pipe: $!\n" unless $pid;
my $output_file = 'output.txt';
open my $my_out, '>', $output_file
or die "Cannot open '$output_file': $!";
while (my $line = <$dir_out>) {
$line =~ s/bytes free/peons liberated/;
print $my_out $line;
}
close $my_out
or die "Cannot close '$output_file': $!";
close $dir_out
or die "Cannot close pipe: $!\n";
Of course, I am assuming there are other things going on in your program and this is only a small part of it. Otherwise, you don't need to write this much code for a simple filter.
You cannot write to FH with print and then expect to read from FH in the next statement. File handles are not FIFOs (by default).
The open gives you a writable file handle, the read end of which is connected to the stdin of dir. Reading from a write file handle just gives you nothing.
What do you actually want to achieve? Send some text to the dir program, or read output of the dir program?
Since in the comment you said you want to read the output of the dir command, you have the open command wrong; use "dir |" instead of "| dir" and read the Perl Open Tutorial.
Maybe this does what you want to do:
open (FH, "dir|") or die "$OS_ERROR";
while (<FH>){
print;
}
print "sometext\n";