I'd like to understand why unlink doesn't work for one specific file

78 views Asked by At

I have a script that creates some temp files for processing later and when they are not needed anymore I want to delete them. The file that won't unlink is always the very last file that's created inside a while loop. Feels like a closing issue, but I can't see it.
Here's the relevant pieces of code:

 # Creates a temp file for each of the guys with every stock item from the products listed against their name.
 open ENG_LIST, 'engineer_list.txt' or die "Can't open engineer_list.txt\n";
 mkdir './Temp';
 while ($line = <ENG_LIST>) {
     chomp $line;
     @products = split(/\|/, $line);
     $engineer = shift @products;
     push @engineer_list, $engineer;
     $fh = "./Temp/$engineer.txt";
     foreach $product (@products) {
         open $output, ">>:raw", "./Temp/$engineer.txt" or die "Unable to open ./Temp/$engineer.txt for appending: $!\n";
         open $input, "<:raw", "./Products/$product.txt" or die "Unable to open ./Products/$product.txt for reading: $!\n";
         print {$output} <$input>;
     }
     close ($fh);
     undef @products;
 }
 close (ENG_LIST);

Then there's some processing I'm happy with. Then the clean up routine:

 # Cleaning up all the temp files.  Need to fix this because it doesn't work!
 @del_files = glob ('./Temp/*.txt');
 foreach $del_file (@del_files) {
     chomp $del_file;
     close ($del_file);
     unlink "$del_file";
 }
 rmtree ('./Temp');

I've tried using chmod, but I'm getting nowhere with that either. The error is always

cannot unlink file - Permission denied
3

There are 3 answers

0
stefan_aus_hannover On

Instead of close ($fh);. You would need to close ($input); and close ($output);. You're trying to delete/ unlink() a file that is in use.

2
Bork On

The argument for close is the same as the first one for open: The name of the file handle. This code:

     $fh = "./Temp/$engineer.txt";
     foreach $product (@products) {
         open $output, ">>:raw", "./Temp/$engineer.txt" or die "Unable to open ./Temp/$engineer.txt for appending: $!\n";
         open $input, "<:raw", "./Products/$product.txt" or die "Unable to open ./Products/$product.txt for reading: $!\n";
         print {$output} <$input>;
     }
     close ($fh);

Should be changed to something like:

use strict;
use warnings;  # always use these

     my $file = "./Temp/$engineer.txt";
     foreach my $product (@products) {
         open my $output, ">>:raw", $file or die "Unable to open $file for appending: $!\n";
         open my $input, "<:raw", "./Products/$product.txt" or die "Unable to open ./Products/$product.txt for reading: $!\n";
         print {$output} <$input>;
     }
    # no close needed

While you can close a file handle explicitly, you can also use a lexical file handle, which is automatically closed when the file handle variable goes out of scope. Which it will do in a loop.

The reason your last file is not deleted is that none of your file handles are ever closed, since you use global variables, and never close them. But you do overwrite them each time you enter that loop. But the last loop remains when all the iterations are done, hence that file handle remains open.

You might also have caught this by checking the return value of unlink

unlink $del_file or warn "Cannot unlink '$del_file': $!";
0
ikegami On

In Windows, many programs (incl Perl's open) open files with share protection that prevents them from being deleted while still open. This results in error EPERM (Permission denied) when someone attempts.

Since you didn't provide the program that results in the error, I can't help any further than pointing out you need to ensure the file handle is closed before you attempt to unlink the file.

Note that the open handle might be in another process. This commonly happens when using files on a One Drive volume, for example, since One Drive locks changed files during synchronization.