Is this a standard Perl language construction or a customization: open HANDLE, ">$fname"

Viewed 60

Not a Perl guru, working with an ancient script, ran into a construct I didn't recognize that yields results I don't expect. Curious whether this is the standard language, or a PM customization of sorts:

open FILE1, ">./$disk_file" or die "Can't open file: $disk_file: $?";

From the looks of this, file is to be opened for writing, but the log error says that file is not found. Perl's file i/o expects 3 parameters, not 2. Log doesn't have the die output, instead saying: "File not found"

Confused a bit here.

EDIT: Made it work using the answers below. Seemed like I was running a cashed version of the .pl for some time, instead of the newly-edited. Finally it caught up with a 2-param open, thanks y'all for your help!

3 Answers

That is the old 2-argument form of open. The second argument is a bit magical:

  • if it starts with '>' the remainder of the string is used as the name of a file to open for writing
  • if it starts with '<' the remainder of the string is used as the name of a file to open for reading (this is the default if '<' is omitted)
  • if it ends with '|' the string up to that point is interpreted as a command which is executed with its STDOUT connected to a pipe which your script will open for reading
  • if it starts with '|' the string after that point is interpreted as a command which is executed with its STDIN connected to a pipe which your script will open for writing This is a potentially security vulnerability because if your script accepts a filename as user input, the user can add a '|' at the beginning or end to trick your script into running a command.

The 3-argument form of open was added in (I think) version 5.8 so it has been a standard part of Perl for a very long time.

The FILE1 part is known as a bareword filehandle - which is a global. Modern style would be to use a lexical scalar like my $file1 instead.

See perldoc perlopen for details but, in brief...

Perl's open() will accept either two or three parameters (there's even a one-parameter version - which no-one ever uses). The two-parameter version is a slightly older style where the open mode and the filename are joined together in the second parameter.

So what you have is equivalent to:

open FILE1, '>', "./$disk_file" or die "Can't open file: $disk_file: $?";

A couple of other points.

  1. We prefer to use lexical variables as filehandles these days (so, open my $file1, ... instead of open FILE1, ...).
  2. I think you'll find that $! will be more useful in the error message than $?. $? contains the error from a child process, but there's no child process here.

Update: And none of this seems to be causing the problems that you're seeing. That seems to be caused by a file actually not being in the expected place. Can you please edit your question to add the exact error message that you're seeing.

The other answers here are correct that's the two-argument syntax. They've done a good job covering why and how you should ideally change it, so I won't rehash here.

However they haven't tried to help you fix it, so let me try that...

This is a guess, but I suspect $disk_file contains a filename with a path (eg my_logs/somelog.log), and the directory part (my_logs in my entirely guessed example) doesn't exists, so is throwing an error. You could create that directory, or alter whatever sets that variable so it's writing to a location that does exist.

Bear in mind these paths will be relative to wherever you're running the script from - not relative to the script itself, so if there's a log directory (or whatever) in the same dir as the script you may want to cd to the script's dir first.

Related