- Apr 28, 2010
- Item 51. Don't ignore the file test operators.
- Item 52. Always use the three-argument open.
- Item 53. Consider different ways of reading from a stream.
- Item 54. Open filehandles to and from strings.
- Item 55. Make flexible output.
- Item 56. Use File::Spec or Path::Class to work with paths.
- Item 57. Leave most of the data on disk to save memory.
Item 52. Always use the three-argument open.
A long time ago, in a Perl far, far away, you had to specify the filehandle mode and the filename together:
open( FILE, '> output.txt' ) || die ...; # OLD and WRONG
That code isn't so bad, but things can get weird if you use a variable for the filename:
open( FILE, $read_file ) || die ...; # WRONG and OLD
Since the data in $read_file can do two jobs, specify the mode and the filename, someone might try to pull a fast one on you by making a weird filename. If they put a > at the beginning of the filename, all of a sudden you've lost your data:
$read_file = '> birdie.txt'; # bye bye birdie!
The two-argument form of open has a magic feature where it interprets these redirection symbols. Unfortunately, this feature can leave your code open to exploits and accidents.
Imagine that the person trying to wreak havoc on your files decides to get a little more tricky. They think that you won't notice when they open a file in read-write mode. This allows the input operator to work on an open file, but also overwrites your data:
$read_file = '
They could even sneak in a pipe, which tells open to run a command:
$read_file = 'rm -rf /
|'; # that's gonna hurt!
And now, just when you think you have everything working, the software trolls come out at three in the morning to ensure that your pager goes off just when you get to sleep.
Since Perl 5.6, you can use the three-argument open to get around this problem. By "can," we mean, "you always will from now on forever and ever."
When you want to read a file, you ensure that you only read from a file:
$read_fileor die ...;
The filename isn't doing double duty anymore, so it has less of a chance of making a mess. None of the characters in $read_file will be special. Any redirection symbols, pipes, or other funny characters are literal characters.
Likewise, when you want to write to a file, you ensure that you get the right mode:
open my ($fh),
'>', $write_file or die ...; open my ($fh),
'>>', $append_file or die ...;
The two-argument form of open protects you from extra whitespace. Part of the filename processing magic lets Perl trim leading and trailing whitespace from the filename. Why would you ever want whitespace at the beginning or end? We won't pretend to know what sorts of crazy things you want. With the three-argument open, you can keep that whitespace in your filename. Try it sometime: make a filename that starts with a new-line. Did it work? Good. We'll let you figure out how to delete it.
Things to remember
- Use the three-argument form of open when you can.
- Use lexical scalars to store filehandle references.
- Avoid precedence problems by using or to check the success of open.