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

From Perl Cookbook (2nd Edition) recipe 7.11 I see that there two main ways to create a temporary file. Use the File::Temp module, or pass undef as the filename to open(), like this ...
open($fh, "+>", undef) or die ...
Is there any reason to believe that one method is more safe and/or reliable than the other?

Thanks!

Replies are listed 'Best First'.
Re: Safe Temp Files
by xdg (Monsignor) on Dec 04, 2006 at 16:10 UTC

    That use of open is only available since 5.8. (See perl58delta.) That may or may not be important to you.

    File::Temp can also be helpful if you need the filename or need the file to persist beyond one open/close cycle, but still be cleaned up later. (Both of which are less "safe" depending on how you're using it.)

    -xdg

    Code written by xdg and posted on PerlMonks is public domain. It is provided as is with no warranties, express or implied, of any kind. Posted code may not have been tested. Use of posted code is at your own risk.

Re: Safe Temp Files
by blue_cowdawg (Monsignor) on Dec 04, 2006 at 16:41 UTC
        Is there any reason to believe that one method is more safe and/or reliable than the other?

    While "safe" might be in the eye of the beholder (and I know I'm going to draw comments on that statement) reliable is another matter.

    First off, I try and write my code in such a way that I don't need temporary files. Leaving surds all over the disk is somthing I don't like to do and I'll go to great lengths to avoid it. My biggest issue with temporary files is they tend to become "permanent" files because quite often either because of oversight on the behalf of the programmer or because Something Bad Happened™ to the process during its lifecycle the temporary files don't get cleaned up after.

    Having asserted that premise I'll go on to say that in the rare occasion that I need a temporary file I tend to use one of two methods.

    Name it by PID

    The most common way I enact a temporary file is to create a filename that is related to both the reason I'm creating it and the process id for example:

    my $unsortedUsersFilename="munger-unsortedusers.$$.txt"; | etc
    In this example, munger is the name of the script doing the work, unsertedusers refers to the fact that it will contain a set of unsorted users (hey! work with me here!) and the $$ is the *nix PID of the script that is creating it.

    During debugging sessions I may well leave off the step of removing the temp file so I can examine this file and know what was going on if the code doesn't work the way I expected it to.

    The don't reinvent the wheel method

    I like using File::Temp once my script has been debugged and I no longer care about the name of the being generated. While the module does provide a templating function for filenames, the names are still random. The biggest reason I like this module is it is platform portable. While 99.999% of my scripting is done on the *nix family of OSs every once in a while I end up writing something on the Evil Empire® platform and so I like things that I write to be portable.

    Hope this at least provides you with food for thought.


    Peter L. Berghold -- Unix Professional
    Peter -at- Berghold -dot- Net; AOL IM redcowdawg Yahoo IM: blue_cowdawg
      Using a PID is easy and commonly used on UNIX, but it might bite you if you port that to Windows if you want the file to survive after the program. On UNIX the PIDs cycle around, and it will be "a long time" before your PID gets reused, but on Windows a PID can get reused by the next process that starts.
            Windows a PID can get reused by the next process that starts.

        Yes, I am aware of that limitation on 'doze. Fortunately for me I don't do a lot of coding on that platform and for the occasion I stick with File::Temp and write the filename to a log file for perusal/retrieval later on.


        Peter L. Berghold -- Unix Professional
        Peter -at- Berghold -dot- Net; AOL IM redcowdawg Yahoo IM: blue_cowdawg
Re: Safe Temp Files
by fmerges (Chaplain) on Dec 04, 2006 at 16:13 UTC

    Hi,

    I would use File::Temp, its compatible with older version of Perl, and is less obscure than this special open call syntax.

    Regards,

    fmerges at irc.freenode.net
Re: Safe Temp Files
by blazar (Canon) on Dec 04, 2006 at 16:24 UTC
    open($fh, "+>", undef) or die ...

    I love that! But one thing that hasn't been mentioned yet, and that may be relevant or not to you is that it opens a handle to an anonymous file. (On filesystems that do support them.)

Re: Safe Temp Files
by ambrus (Abbot) on Dec 04, 2006 at 17:09 UTC

    The advantage of File::Temp is that you can use it if you need the name of the temp file. The advantage of open is that its syntax is simpler.

      advantage of open is that its syntax is simpler

      I'm not convinced of that. Apart from the "use" line, the File::Temp approach is about the same length and the intent is more immediately obvious.

      open(my $fh, "+>", undef) or die ...
      use File::Temp (); my $fh = File::Temp->new or die ...

      -xdg

      Code written by xdg and posted on PerlMonks is public domain. It is provided as is with no warranties, express or implied, of any kind. Posted code may not have been tested. Use of posted code is at your own risk.

        the File::Temp approach is about the same length and the intent is more immediately obvious

        I disagree on the latter. new is the worst name for a method, for a module like this, so a line like this:

        my $fh = File::Temp->new
        is always enough to make me go "WTF...?".

        I wish the module author had put some more inspiration in the name for the constructor, like Tim Bunce used "connect" in DBI instead of the plain, boring and somewhat obscure "new". I like this a lot better.

Re: Safe Temp Files
by gmock (Acolyte) on Dec 05, 2006 at 14:57 UTC
    Thank you all for your kind replies. But I don't see where my basic question was answered.

    Which method is safer and/or more reliable? I think they are about the same. Am I correct?

    Thanks!

    -George

      I would recommend using File::Temp. I think it is equally safe/reliable, gives more portability (to older Perls), and gives more flexibility.

      For example, if you want your temporary files to stick around for inspection during debugging, you can just tell File::Temp not to unlink them on exit if debugging is on.

      use constant DEBUG => 1; my $fh = File::Temp->new( UNLINK => DEBUG ? 0 : 1 );

      If you're in the habit of using File::Temp, you'll always have the full flexibility it gives available to you.

      -xdg

      Code written by xdg and posted on PerlMonks is public domain. It is provided as is with no warranties, express or implied, of any kind. Posted code may not have been tested. Use of posted code is at your own risk.

      Can you be more specific about what safe and reliable mean to you? For instance is saftey a matter of hacker-proofing, like the way CGI unlinks it's file handles to avoid spying by others? Is reliability a matter of portability from one OS to another or is it about always getting a unique filename in each situation? Clarifying these might lead to more specific opinions.
Re: Safe Temp Files
by Cabrion (Friar) on Dec 05, 2006 at 12:50 UTC
    You might check out how the CGI module implements temp-files. It takes things a step further and unlinks the file after an open() wich makes it harder for another process to intercept the file handle.
      You might check out how the CGI module implements temp-files. It takes things a step further and unlinks the file after an open() wich makes it harder for another process to intercept the file handle.

      But that's exactly what open my $fh, "+>", undef does. (Under osen that permit it.)

      stout:~ [16:28:32]$ strace -eopen,unlink perl -e 'open $f, "+>", undef +' open("/etc/ld.so.preload", O_RDONLY) = 3 open("/etc/ld.so.cache", O_RDONLY) = 3 open("/lib/libpthread.so.0", O_RDONLY) = 3 open("/lib/libnsl.so.1", O_RDONLY) = 3 open("/lib/libdl.so.2", O_RDONLY) = 3 open("/lib/libm.so.6", O_RDONLY) = 3 open("/lib/libcrypt.so.1", O_RDONLY) = 3 open("/lib/libutil.so.1", O_RDONLY) = 3 open("/lib/libc.so.6", O_RDONLY) = 3 open("/dev/urandom", O_RDONLY|O_LARGEFILE) = 3 open("/dev/null", O_RDONLY|O_LARGEFILE) = 3 open("/tmp/PerlIO_dr5fu4", O_RDWR|O_CREAT|O_EXCL|O_LARGEFILE, 0600) = +3 unlink("/tmp/PerlIO_dr5fu4") = 0 Process 17389 detached

      Also see:

      stout:~ [16:29:17]$ perl -le 'open $f, "+>", undef; system "ls -l /pro +c/$$/fd"' total 5 lrwx------ 1 blazar users 64 Dec 6 16:29 0 -> /dev/pts/0 lrwx------ 1 blazar users 64 Dec 6 16:29 1 -> /dev/pts/0 lrwx------ 1 blazar users 64 Dec 6 16:29 2 -> /dev/pts/0 lrwx------ 1 blazar users 64 Dec 6 16:29 3 -> /tmp/.nfs00364089000002 +21 lr-x------ 1 blazar users 64 Dec 6 16:29 4 -> pipe:[12410097]

      (That's because /tmp is nfs mounted here, under other fs's it would explicitly tell you it has been deleted - at least IIRC it did when I tried it.)