Back to Top

Sunday, April 22, 2007

Cleaning it all up - temporary files in Perl

One of the most frustrating things in programming is doing all of the extra plumbing. You can just say (if you are trying to create a stable product): open file A, read a line, transform it and dump it to file B. You have to think about all the error conditions which may appear: what if file A can't be opened? what if file B can't be opened? what if the read line is invalid? what is the rollback strategy for every possible situation? This is why things like Ruby on Rails or garbage collected languages are so popular: because they take care of the details in the background, without the programmer having to explicitly think about them.

One detail which comes up frequently in scripts is the creation of temporary files which should be removed once the scrip terminates. One easy (and cross platform) way to do this is to use the File::Temp module or you could write an END block. However both of these methods fail to delete the file if at the moment when the cleanup is being performed the file is being read by an other process. The Windows API offers a very nice way of dealing with such a problem: the FILE_FLAG_DELETE_ON_CLOSE flag for the CreateFile API (which is the function used to create / open files - think of it as a lower level version of open). What this flag does is it tells Windows that the file should be deleted when the last handle to it gets closed. This means that we can open it from multiple processes and when the last open handle is closed, the operating system automagically deletes the file. Below you can find two code samples, one for creating the file and one for reading it from an other process. The drawback of this method is that its not portable and that you have to use the Windows APIs to access the files, because open can't produce the required flags.

use Win32API::File qw/:Func :FILE_FLAG_ :FILE_SHARE_/;

use strict;

use warnings;

my $fh = createFile("file.tmp", 
"wc", "", { Attributes=>"t", Flags=>FILE_FLAG_DELETE_ON_CLOSE, Share => FILE_SHARE_READ });

WriteFile($fh, $contents, length($contents), [], []);

my $fh = CreateFile($fileName, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | 4, [], OPEN_EXISTING, 0, 0);
ReadFile($fh, $buff, 8192, [], []);

Two more interesting things: the OS takes care of closing the handles when the process exists, so there is no need for doing it in the write example. And whenever the API documentation says that NULL can be passed in for a particular parameter, the corresponding Perl construct is a reference to an empty array ([]). This was probably chosen because undef's are stripped from lists (meaning that (1, undef, undef, undef, 2) is (1, 2), so we can't tell at which positions were the undef's located).

And also an useful link: Precompiled regular expressions from the Windows Perl Blog.


Post a Comment

You can use some HTML tags, such as <b>, <i>, <a>. Comments are moderated, so there will be a delay until the comment appears. However if you comment, I follow.