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

I'm using File::Temp::tempfile to get a name to use for a temporary file to temporary output from a piped command. The book Programming Perl, 4th ed. mentions that it is prefereable to use the file handle that the tempfile() function creates instead of the file name. Is there any way to do that in the context of the code snippet below?

my ($handle, $output) = tempfile("tidyXXXXXX", DIR => "/run/validator", SUFFIX => ".dat" ); my $tidy = q(/usr/bin/tidy); my @args = ('-quiet', '-xml', '-file', $output); # process web page with tidy open(TIDY, "-|", $tidy, @args, $tempfile) or die("Could not open '$tidy' : $!\n"); close(TIDY);

What about other improvements to the above?

Replies are listed 'Best First'.
Re: File::Temp::tempfile : name versus handle
by haukex (Archbishop) on Mar 22, 2018 at 13:22 UTC

    I don't have my Camel handy at the moment, but I believe the major issue with File::Temp occurs when you request only a filename, and not a handle, e.g. by setting OPEN=>0, because then you easily open yourself up to race conditions. IIRC, using the my ($filehandle, $filename) = tempfile(); form, even if all you do is immediately close $filehandle; and then use just the name, should be relatively safe if all the programs working in the temporary directory are well-behaved and the filesystem itself is well-behaved. (But make sure to read the entirety of the File::Temp docs.)

    I'm a little confused about your code snippet, why do you open TIDY and then immediately close it again (without checking for errors, BTW)? tidy supports writing to and from files directly, which is what you appear to be doing, so it might be better to just use system instead of a piped open. Also, if you could explain what you are doing with the data before and after the tidy invocation, that would be helpful - e.g. what is $tempfile, do you need the data to reside in a regular file before and/or after the operation or do you just need it as strings in your program, etc. Depending on those requirements, it might be easier to just use IPC::Run3 (which does most of its work with temp files behind the scenes anyway). (I wrote at length about calling external commands here.)

      Ok. Thanks for the clarification. I'm also reviewing the manual page for File::Temp again to see what I may have missed the earlier times. It often helps to try re-reading on a different display device.

      As for the code snippet, the pipe gets opened and closed immediately because as it stands the output from the process it calls goes to the temporary file which is read later on further down in the script. The only errors (I think) that could be caught from the open/close itself would be handled by the or die() part. The reason for a two-step approach is that otherwise the diagnostic messages from the program, "tidy", go to stderr and I have not found a way to redirect stderrr properly under Fast-CGI. So I figure the temporary file is the way to go and to leave stderr alone. I tried a lot of ways to redirect stderr, documented and made up, which all work from the shell, but do not work via Fast-CGI.

      The data does not need to be in a file, it can be read as strings maybe better. It's just that the stderr issue with Fast::CGI seems to prevent that.

        The only errors (I think) that could be caught from the open/close itself would be handled by the or die() part.

        No, sorry, with a piped open you need to check close for errors as well, as described in close.

        I have not found a way to redirect stderrr properly under Fast-CGI

        I unfortunately don't have the time right now to spin up a FastCGI environment, but I would suggest trying both IPC::Run3, as well as a system call inside of Capture::Tiny's capture (as shown in its docs). I've had very good experiences with both of those modules.

        As for the code snippet, the pipe gets opened and closed immediately because as it stands the output from the process it calls goes to the temporary file which is read later on further down in the script.

        While I'm still having a bit of trouble matching your description to your code (maybe I'm just being dense at the moment), what I am getting from your code is that perhaps tidy is sending things to its STDOUT that you want to suppress using the piped open? Since the modules I named above capture both STDOUT and STDERR, again I'd suggest giving them a try.

Re: File::Temp::tempfile : name versus handle
by ikegami (Patriarch) on Mar 22, 2018 at 16:25 UTC

    The problem happens if you write to a directory to which others have write access. They could swap your file for one of their own, even if they don't have access to your file. This can be a real problem for setuid scripts.

    Solutions would be system-specific. On a linux system, for example, you could dup the temp file onto fd 3, make sure it's not close-on-exec, and pass /proc/self/fd/3 for the file name.

      Thanks, that's a good point about the directory permissions. I already have the temporary files in a unique directory to keep them isolated. I did it more of reflex than conscious decision to its nice to have the reminder.

Re: File::Temp::tempfile : name versus handle
by thanos1983 (Parson) on Mar 22, 2018 at 16:10 UTC

    Hello mldvx4,

    Read also Using Temporary Files in Perl contains some information regarding your problem.

    BR / Thanos

    Seeking for Perl wisdom...on the process of learning...not there...yet!
Re: File::Temp::tempfile : name versus handle
by karlgoethebier (Abbot) on Mar 23, 2018 at 12:55 UTC
    "...other improvements to the above?"

    HTML::Tidy?

    Best regards, Karl

    «The Crux of the Biscuit is the Apostrophe»

    perl -MCrypt::CBC -E 'say Crypt::CBC->new(-key=>'kgb',-cipher=>"Blowfish")->decrypt_hex($ENV{KARL});'Help