Perl open files with a variable in the file name

I am trying to open a list of files in a folder. In the end, I want to insert an HTML fragment into it. So far I can open the folder and read the list in the array. Good. But when I try to open files using a variable as a file name, I get an error message every time ("permission denied"). Regardless of whether I use $ _ or other options for placing the values โ€‹โ€‹of list items in variables. I have found similar questions here, but have not yet found a solution. Here is my code:

use strict; use warnings; my ($line); opendir (FOLDER,"path/to/folder/./") or die "$!"; my @folderlist = readdir(FOLDER); my @sorted_folderlist = sort @folderlist; close(FOLDER); foreach (@sorted_folderlist) { my $filename = $_; open (READ, ">", "path/to/folder/$filename") or die "$!"; # do something close (READ); } 

What is the mistake here? And how can I open files using a variable as a file name?

Pjoern

Here is my modified code to answer 1:

 my $dh; opendir $dh, "dir/./" or die ... my @folderlist = grep { -f "dir/$_" } readdir $dh; close $dh; my @sorted_folderlist = sort @folderlist; foreach my $filename (@sorted_folderlist) { open my $fh, "<", "dir/$filename" or die ... open my $writeto, ">", "new/$filename" or die ... print $writeto "$fh"; close $fh; close $writeto; } 
+5
source share
2 answers

There are several problems in your code.

First, the cause of the error is probably that readdir() also returns . and .. but these are directories. So you are trying to write directory/. .

Secondly, your error message contains $! which is good, but you do not display the file name. Then you would see your mistake.

Thirdly, you call the file descriptor that you open to write READ .

In addition, you should now use lexical file descriptors.

 use strict; use warnings; opendir my $dh, "dir" or die $!; # read all files, not directories my @folderlist = grep { -f "dir/$_" } readdir $dh; close $dh; my @sorted_folderlist = sort @folderlist; foreach my $filename (@sorted_folderlist) { open my $fh, ">", "dir/$filename" or die "Could not write to dir/$filename: $!"; print $fh "foo...\n"; close $fh; } 
+8
source

You have mistakes and good practices clarified in tinita answer .

I would like to comment on a possible improvement in order to create a list of files with full paths to begin with.

  • Use map on readdir output to add a directory to each file, then filter

     my @files = grep { -f } map { "$dir/$_" } readdir $dh; 

    or by implementing grep (filtering) in the map block

     my @files = map { my $fp = "$dir/$_"; -f $fp ? $fp : () } readdir $dh; 

    Here, if $fp not -f , we return an empty list that is smoothed out, thus disappearing.

  • Use glob , which gives you the full path & dagger;

     use File::Glob ':bsd_glob'; my @files = grep { -f } glob "$dir/*"; 

    where File :: Glob bsd_glob() overrides glob and nicely handles spaces in names.

Note the difference: * in glob does not select records starting with a period ( . ). If you also want to, change it to glob "$dir/{*,.*}" . See Link File::Glob docs.


Update fix editing (adding) to a question also discussed in comments

After the full path file names have been read in @files

 foreach my $file (@files) { my $out_file = 'new_' . $file; open my $writeto '>', $outfile or die "Can't open $outfile: $!"; open my $fh, '<', $file or die "Can't open $file: $!"; while (my $line = <$fn>) { # process $line and build what need be written to the new file my $new_line = ... print $writeto $new_line; } close $writeto; close $fh; } 

(Or, if the file names are not the full path, create the full path in the open call.)

When you open it for the first time, the file descriptors are closed, so for the latest files explicitly close is required. You can then declare the variables my ($fh, $writeto); before the loop, use open $fh ... (no my ) and close the file descriptors after it. But then you have these variables outside the foreach scope.

Documentation:


& dagger; glob "$dir/*" has the ability to inject, for very specific directory names. Although this can only happen with rather unusual names, it is safer to use an escape sequence

 my @files = grep { -f } glob "\Q$dir\E/*" 

Since it also runs through spaces, File::Glob with its bsd_glob() not required.

+6
source

Source: https://habr.com/ru/post/1274110/


All Articles