http://qs321.pair.com?node_id=366865

shay has asked for the wisdom of the Perl Monks concerning the following question:

What is the best way to copy a file to a temporary file?

(The purpose of the temporary file is to read data from, process it in some way, and then write it back over the top of the original file; i.e. I effectively want to edit the original file "in-place", but not all in memory at once in case it is too large.)

I was intending to use the File::Temp::tempfile() function to get a temporary file, and File::Copy to do the copying, but both of the following obvious ideas have problems:

  1. my $tmpfh = File::Temp::tempfile(); File::Copy::copy($file, $tmpfh);
  2. my($tmpfh, $tmpfile) = File::Temp::tempfile(UNLINK => 1); File::Copy::copy($file, $tmpfile);
The first idea is risky according to File::Copy's manpage (it says passing filehandles instead of filenames to copy() may lead to loss of information on some systems), while the second idea is not the recommended practice in File::Temp's manpage (it is safer to only get the temporary file's filehandle, not to get it's name as well).

Replies are listed 'Best First'.
Re: Copying a file to a temporary file
by tachyon (Chancellor) on Jun 15, 2004 at 13:15 UTC
Re: Copying a file to a temporary file
by Aragorn (Curate) on Jun 15, 2004 at 13:26 UTC
    According to the docs of File::Temp, if you use File::Temp::tempfile in scalar context, it only returns the filehandle. You can then use this filehandle as an argument to File::Copy::copy, which accepts both filenames and filehandles. Update: You'll have to find out if the warning in File::Copy's docs about using filehandles apply to your situation.

    Arjen

Re: Copying a file to a temporary file
by BrowserUk (Patriarch) on Jun 15, 2004 at 13:26 UTC

    Is there a reason why you want to copy the original file to process it rather than say, renaming it to some temporary name and outputting the results of your munging to a new file with the original name?

    For example: Is it necessary that the original file be available to other processes whilst the munging is in progress?

    The nice thing about ranaming is that it is (under most circumstances), an atomic operation at the OS level, which closes many possibilities for problems.


    Examine what is said, not who speaks.
    "Efficiency is intelligent laziness." -David Dunham
    "Think for yourself!" - Abigail
      What I want to achieve is editing a file in-place (via a temporary file) and optionally creating a backup file as well if a backup filename is given.

      My idea was therefore to use the backup file as the temporary file, where a backup filename is given, or else use File::Temp to create one for me. I then copy to the temporary file, read from it and write back over the original, and then leave File::Temp to clean up the temporary file if one was created. (If a specified backup file was used instead, then it gets left afterwards, of course.)

      I could start by renaming the original to the backup/temporary name instead, as you suggest, but where do I get the temporary name from? File::Temp returns an open filehandle - no good for renaming my original file to, hence I was looking to copy to it instead.

      Actually, having read Re-runnably editing a file in place, I'm now thinking something along those lines would be better:

      I could get a temporary filehandle, read from the original file, process the data and write to the temporary filehandle. Then I'd want to rename the temporary file to the original filename, but I don't know the temporary filename unless I ignore File::Temp's advice and pick up both the handle and the name. Maybe that's safe enough since I wouldn't be doing anything with the temporary filename except renaming it (and I therefore wouldn't want File::Temp to try to delete the temporary file either). (I'd have to create the backup file separately, rather than using it as the temporary file, in this scheme, of course.)

      - Steve

        I think I'd use something simple like:

        my $file = ...; my $n=0; if( -e $file ) { ## Stop endless loop if $file doesn't exist $n++ until rename $file, "$file.bak$n"; } else { die "$file doesn't exist"; } my $backup = "$file.bak$n";

        Examine what is said, not who speaks.
        "Efficiency is intelligent laziness." -David Dunham
        "Think for yourself!" - Abigail
        "Memory, processor, disk in that order on the hardware side. Algorithm, algoritm, algorithm on the code side." - tachyon