I am using File::Find to run through a directory tree and when I try to open the current file for reading I get No such file or directory. This happens with ALL files in the directory tree.
Here's the sub I use in the find():
sub {
if (-d) {
return;
}
if (-f) {
my $file = ${File::Find::name};
open (my $IN, '<', '$file') or die "$!\n";
while (<$IN>) {
### Do some formatting.
}
close $IN;
}
}
It fails in the line:
open (my $IN, '<', '$file') or die "$!\n";
I thought it's a matter of links maybe, but even with follow => 1 option I get this error.
By the way, without follow the error I get is on the first file of the first directory I find and with it, the error is on the last file of the last directory (but in both cases, it's on the first file inspected by File::Find).
Problem solved. Apparently, replacing the single quotes with double quotes in the open line, or even better, not using any quotes, did the trick. The string literal '$file' produces the string $file, and there's clearly no file with this name.
Related
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.
I wrote a program to check for misspellings or unused data in a text file. Now, I want to check all files in a directory using the same process.
Here are a few lines of the script that I run for the first file:
open MYFILE, 'checking1.txt' or die $!;
#arr_file = <MYFILE>;
close (MYFILE);
open FILE_1, '>text1' or die $!;
open FILE_2, '>Output' or die $!;
open FILE_3, '>Output2' or die $!;
open FILE_4, '>text2' or die $!;
for ($i = 0; $i <= $#arr_file; $i++) {
if ( $arr_file[$i-1] =~ /\s+\S+\_name\s+ (\S+)\;/ ) {
print FILE_1 "name : $i $1\n";
}
...
I used only one file, checking1.txt, to execute the script, but now I want to do the same process for all files in the all_file_directory
Use an array to store file names and then loop over them. At the end of loop rename output files or copy them somewhere so that they do not get overwritten in next iteration.
my #files = qw(checking1.txt checking2.txt checking3.txt checking4.txt checking5.txt);
foreach my $filename (#files){
open (my $fh, "<", $filename) or die $!;
#perform operations on $filename using filehandle $fh
#rename output files
}
Now for the above to work you need to make sure the files are in the same directory. If not then:
Provide absolute path to each file in #files array
Traverse directory to find desired files
If you want to traverse the directory then see:
How do I read in the contents of a directory in Perl?
How can I recursively read out directories in Perl?
Also:
Use 3 args open
Always use strict; use warnings; in your Perl program
and give proper names to the variables. For eg:
#arr_file = <MYFILE>;
should be written as
#lines = <MYFILE>;
Your all files in same directory means put the program inside the directory then run it.
For read the file from a directory use glob
while (my $filename =<*.txt>) # change the file extension whatever you want
{
open my $fh, "<" , $filename or die "Error opening $!\n";
#do your stuff here
}
Why not useFile::Find? Makes changing files in directories very easy. Just supply the start directory.
It's not always the best choice, depends on your needs, but it's useful and easy almost every time I need to modify a lot of files all at once.
Another option is to just loop through the files, but in this case you'll have to supply the file names.
As mkHun pointed out a glob can be helpful.
Although I found many posts on how to open a file in a for loop in perl, I am having a specific issue in creating a file within a directory ( which is also the array variable)-
I am opening a file using
foreach my $dir (#listofdirs) {
open (my $OUTFILE, '>', "$dir/$dir.txt") or die "$!";
, this does not create a file and gives me an error No such file or directory.
If i just use open (my $OUTFILE, '>', "$dir.txt") or die; It works and creates a file under main directory from where I execute the script.
How can I control/specify the path so that it opens a file inside each $dir variable (directory)? I am sorry if this has been addressed earlier, but I am not sure what is the right way to specify the path for the new files.
Edit -
Can I change directory where the file is being created inside the loop and assign it the $dir variable value everytime?
Without seeing your error message, I have a pretty good idea what's wrong.
foreach my $dir (#listofdirs) {
open (my $OUTFILE, '>', "$dir/$dir.txt") or die;
...
}
I'm going to guess #listofdirs contains things like /foo/bar or foo/bar/baz/ and thus "$dir/$dir.txt" will create some funny filepaths.
$dir "$dir/$dir.txt"
/foo/bar /foo/bar//foo/bar.txt
foo/bar/ foo/bar//foo/bar/.txt
foo/ foo//foo/.txt
Most of these aren't going to work for various reasons. /foo/bar//foo/bar.txt will require that the directory /foo/bar/foo/ already exists. foo/bar//foo/bar/.txt is an invalid path.
"$dir/$dir.txt" is a funny construct anyway. Are you sure that's what you meant to do?
To figure out what's gone wrong you can add an some information to your error message. The traditional way is to write it all out long hand on every open call.
foreach my $dir (#listofdirs) {
open (my $OUTFILE, '>', "$dir/$dir.txt")
or die "Can't open '$dir/$dir.txt': $!";
...
}
Now you'll see what it tried to open, and why it failed (contained in $!). This rapidly gets tiresome and inconsistent, so it's better to let autodie do it for you.
# at the top of your code along with things like "use strict"
use autodie;
...
foreach my $dir (#listofdirs) {
open (my $OUTFILE, '>', "$dir/$dir.txt");
...
}
Why program can't open the file? i.e dies. I searched for this problem, but it seems all fine to me.
Funny thing is this code worked before, and i don't think i changed something from that moment in open function.
my $i;
my $regex = $ARGV[0];
for (#ARGV[1 .. $#ARGV]){
open (my $fh, "<", "$_") or die ("Can't open, $!");
$i++;
foreach (<$fh>){
print "Given regexp: $regex\nfile$i:\n line $.: $1\n" if $_ =~ /(\b$regex\b)/;
}
}
OUTPUT:
Can't open Not a directory
Not a directory means you're supplying an argument that assumes a non-directory is a directory.
For instance, if your argument is
a/b
and
a
exists but is not a directory, you will get this error.
Check your argument. it should be a proper directory name
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