Opening Files with Unusual Filenames

Problem

You want to open a file with a funny filename, like "-" or one that starts with <, >, or |, has leading or trailing whitespace; or ends with |. You don’t want these to trigger open’s do-what-I-mean behavior, since in this case, that’s not what you mean.

Solution

Use open like this:

$filename =~ s#^(\s)#./$1#;
open(HANDLE, "< $filename\0")          or die "cannot open $filename : $!\n";

Or simply use sysopen:

sysopen(HANDLE, $filename, O_RDONLY)   or die "cannot open $filename: $!\n";

Discussion

The open function uses a single string to determine both the filename and the mode—the way the file is to be opened. If your filename begins with the characters used to indicate the mode, open can easily do something unexpected. Imagine the following code:

$filename = shift @ARGV;
open(INPUT, $filename)               or die "Couldn't open $filename : $!\n";

If the user gave ">/etc/passwd" as the filename on the command line, this code would attempt to open /etc/passwd for writing—definitely unsafe! We can try to give an explicit mode, say for writing:

open(OUTPUT, ">$filename")
    or die "Couldn't open $filename for writing: $!\n";

but even this would let the user give a filename of ">data" and the code would append to the file data instead of erasing the old contents.

The easiest solution is sysopen, which takes the mode and filename as separate arguments:

use Fcntl; # for file constants sysopen(OUTPUT, $filename, O_WRONLY|O_TRUNC) or die "Can't open $filename for writing: $!\n"; ...

Get Perl Cookbook now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.