ASPN ActiveState Programmer Network
  ActiveState
/ Home / Perl / PHP / Python / Tcl / XSLT /
/ Safari / My ASPN /
Cookbooks | Documentation | Mailing Lists | Modules | News Feeds | Products | User Groups | Web Services
SEARCH
advanced | search help

Reference
ActivePerl 5.10
Core Documentation
perl
perlintro
perltoc
perlreftut
perldsc
perllol
perlrequick
perlretut
perlboot
perltoot
perltooc
perlbot
perlstyle
perlcheat
perltrap
perldebtut
perlfaq
perlfaq1
perlfaq2
perlfaq3
perlfaq4
perlfaq5
perlfaq6
perlfaq7
perlfaq8
perlfaq9
perlsyn
perldata
perlop
perlsub
perlfunc
perlopentut
perlpacktut
perlpod
perlpodspec
perlrun
perldiag
perllexwarn
perldebug
perlvar
perlre
perlrebackslash
perlrecharclass
perlreref
perlref
perlform
perlobj
perltie
perldbmfilter
perlipc
perlfork
perlnumber
perlthrtut
perlothrtut
perlport
perllocale
perluniintro
perlunicode
perlunifaq
perlunitut
perlebcdic
perlsec
perlmod
perlmodlib
perlmodstyle
perlmodinstall
perlnewmod
perlpragma
perlutil
perlcompile
perlfilter
perlglossary
perlembed
perldebguts
perlxstut
perlxs
perlclib
perlguts
perlcall
perlreapi
perlreguts
perlapi
perlintern
perliol
perlapio
perlhack
perlbook
perlcommunity
perltodo
perldoc
perlhist
perldelta
perl5100delta
perl595delta
perl594delta
perl593delta
perl592delta
perl591delta
perl590delta
perl588delta
perl587delta
perl586delta
perl585delta
perl584delta
perl583delta
perl582delta
perl581delta
perl58delta
perl573delta
perl572delta
perl571delta
perl570delta
perl561delta
perl56delta
perl5005delta
perl5004delta
perlartistic
perlgpl
perlcn
perljp
perlko
perltw
perlaix
perlamiga
perlapollo
perlbeos
perlbs2000
perlce
perlcygwin
perldgux
perldos
perlepoc
perlfreebsd
perlhpux
perlhurd
perlirix
perllinux
perlmachten
perlmacos
perlmacosx
perlmint
perlmpeix
perlnetware
perlopenbsd
perlos2
perlos390
perlos400
perlplan9
perlqnx
perlriscos
perlsolaris
perlsymbian
perltru64
perluts
perlvmesa
perlvms
perlvos
perlwin32

MyASPN >> Reference >> ActivePerl 5.10 >> Core Documentation
ActivePerl 5.10 documentation

NAME

perlopentut - tutorial on opening things in Perl


DESCRIPTION

Perl has two simple, built-in ways to open files: the shell way for convenience, and the C way for precision. The shell way also has 2- and 3-argument forms, which have different semantics for handling the filename. The choice is yours.


Open à la shell

Perl's open function was designed to mimic the way command-line redirection in the shell works. Here are some basic examples from the shell:

    $ myprogram file1 file2 file3
    $ myprogram    <  inputfile
    $ myprogram    >  outputfile
    $ myprogram    >> outputfile
    $ myprogram    |  otherprogram 
    $ otherprogram |  myprogram

And here are some more advanced examples:

    $ otherprogram      | myprogram f1 - f2
    $ otherprogram 2>&1 | myprogram -
    $ myprogram     <&3
    $ myprogram     >&4

Programmers accustomed to constructs like those above can take comfort in learning that Perl directly supports these familiar constructs using virtually the same syntax as the shell.

Simple Opens

The open function takes two arguments: the first is a filehandle, and the second is a single string comprising both what to open and how to open it. open returns true when it works, and when it fails, returns a false value and sets the special variable $! to reflect the system error. If the filehandle was previously opened, it will be implicitly closed first.

For example:

    open(INFO,      "datafile") || die("can't open datafile: $!");
    open(INFO,   "<  datafile") || die("can't open datafile: $!");
    open(RESULTS,">  runstats") || die("can't open runstats: $!");
    open(LOG,    ">> logfile ") || die("can't open logfile:  $!");

If you prefer the low-punctuation version, you could write that this way:

    open INFO,   "<  datafile"  or die "can't open datafile: $!";
    open RESULTS,">  runstats"  or die "can't open runstats: $!";
    open LOG,    ">> logfile "  or die "can't open logfile:  $!";

A few things to notice. First, the leading less-than is optional. If omitted, Perl assumes that you want to open the file for reading.

Note also that the first example uses the || logical operator, and the second uses or, which has lower precedence. Using || in the latter examples would effectively mean

    open INFO, ( "<  datafile"  || die "can't open datafile: $!" );

which is definitely not what you want.

The other important thing to notice is that, just as in the shell, any whitespace before or after the filename is ignored. This is good, because you wouldn't want these to do different things:

    open INFO,   "<datafile"   
    open INFO,   "< datafile" 
    open INFO,   "<  datafile"

Ignoring surrounding whitespace also helps for when you read a filename in from a different file, and forget to trim it before opening:

    $filename = <INFO>;         # oops, \n still there
    open(EXTRA, "< $filename") || die "can't open $filename: $!";

This is not a bug, but a feature. Because open mimics the shell in its style of using redirection arrows to specify how to open the file, it also does so with respect to extra whitespace around the filename itself as well. For accessing files with naughty names, see Dispelling the Dweomer.

There is also a 3-argument version of open, which lets you put the special redirection characters into their own argument:

    open( INFO, ">", $datafile ) || die "Can't create $datafile: $!";

In this case, the filename to open is the actual string in $datafile, so you don't have to worry about $datafile containing characters that might influence the open mode, or whitespace at the beginning of the filename that would be absorbed in the 2-argument version. Also, any reduction of unnecessary string interpolation is a good thing.

Indirect Filehandles

open's first argument can be a reference to a filehandle. As of perl 5.6.0, if the argument is uninitialized, Perl will automatically create a filehandle and put a reference to it in the first argument, like so:

    open( my $in, $infile )   or die "Couldn't read $infile: $!";
    while ( <$in> ) {
        # do something with $_
    }
    close $in;

Indirect filehandles make namespace management easier. Since filehandles are global to the current package, two subroutines trying to open INFILE will clash. With two functions opening indirect filehandles like my $infile, there's no clash and no need to worry about future conflicts.

Another convenient behavior is that an indirect filehandle automatically closes when it goes out of scope or when you undefine it:

    sub firstline {
        open( my $in, shift ) && return scalar <$in>;
        # no close() required
    }

Pipe Opens

In C, when you want to open a file using the standard I/O library, you use the fopen function, but when opening a pipe, you use the popen function. But in the shell, you just use a different redirection character. That's also the case for Perl. The open call remains the same--just its argument differs.

If the leading character is a pipe symbol, open starts up a new command and opens a write-only filehandle leading into that command. This lets you write into that handle and have what you write show up on that command's standard input. For example:

    open(PRINTER, "| lpr -Plp1")    || die "can't run lpr: $!";
    print PRINTER "stuff\n";
    close(PRINTER)                  || die "can't close lpr: $!";

If the trailing character is a pipe, you start up a new command and open a read-only filehandle leading out of that command. This lets whatever that command writes to its standard output show up on your handle for reading. For example:

    open(NET, "netstat -i -n |")    || die "can't fork netstat: $!";
    while (<NET>) { }               # do something with input
    close(NET)                      || die "can't close netstat: $!";

What happens if you try to open a pipe to or from a non-existent command? If possible, Perl will detect the failure and set $! as usual. But if the command contains special shell characters, such as > or *, called 'metacharacters', Perl does not execute the command directly. Instead, Perl runs the shell, which then tries to run the command. This means that it's the shell that gets the error indication. In such a case, the open call will only indicate failure if Perl can't even run the shell. See How can I capture STDERR from an external command? in the perlfaq8 manpage to see how to cope with this. There's also an explanation in the perlipc manpage.

If you would like to open a bidirectional pipe, the IPC::Open2 library will handle this for you. Check out Bidirectional Communication with Another Process in the perlipc manpage

The Minus File

Again following the lead of the standard shell utilities, Perl's open function treats a file whose name is a single minus, "-", in a special way. If you open minus for reading, it really means to access the standard input. If you open minus for writing, it really means to access the standard output.

If minus can be used as the default input or default output, what happens if you open a pipe into or out of minus? What's the default command it would run? The same script as you're currently running! This is actually a stealth fork hidden inside an open call. See Safe Pipe Opens in the perlipc manpage for details.

Mixing Reads and Writes

It is possible to specify both read and write access. All you do is add a "+" symbol in front of the redirection. But as in the shell, using a less-than on a file never creates a new file; it only opens an existing one. On the other hand, using a greater-than always clobbers (truncates to zero length) an existing file, or creates a brand-new one if there isn't an old one. Adding a "+" for read-write doesn't affect whether it only works on existing files or always clobbers existing ones.

    open(WTMP, "+< /usr/adm/wtmp") 
        || die "can't open /usr/adm/wtmp: $!";
    open(SCREEN, "+> lkscreen")
        || die "can't open lkscreen: $!";
    open(LOGFILE, "+>> /var/log/applog")
        || die "can't open /var/log/applog: $!";

The first one won't create a new file, and the second one will always clobber an old one. The third one will create a new file if necessary and not clobber an old one, and it will allow you to read at any point in the file, but all writes will always go to the end. In short, the first case is substantially more common than the second and third cases, which are almost always wrong. (If you know C, the plus in Perl's open is historically derived from the one in C's fopen(3S), which it ultimately calls.)

In fact, when it comes to updating a file, unless you're working on a binary file as in the WTMP case above, you probably don't want to use this approach for updating. Instead, Perl's -i flag comes to the rescue. The following command takes all the C, C++, or yacc source or header files and changes all their foo's to bar's, leaving the old version in the original filename with a ".orig" tacked on the end:

    $ perl -i.orig -pe 's/\bfoo\b/bar/g' *.[Cchy]

This is a short cut for some renaming games that are really the best way to update textfiles. See the second question in the perlfaq5 manpage for more details.

Filters

One of the most common uses for open is one you never even notice. When you process the ARGV filehandle using <ARGV>, Perl actually does an implicit open on each file in @ARGV. Thus a program called like this:

    $ myprogram file1 file2 file3

can have all its files opened and processed one at a time using a construct no more complex than:

    while (<>) {
        # do something with $_
    }

If @ARGV is empty when the loop first begins, Perl pretends you've opened up minus, that is, the standard input. In fact, $ARGV, the currently open file during <ARGV> processing, is even set to "-" in these circumstances.

You are welcome to pre-process your @ARGV before starting the loop to make sure it's to your liking. One reason to do this might be to remove command options beginning with a minus. While you can always roll the simple ones by hand, the Getopts modules are good for this:

    use Getopt::Std;
    # -v, -D, -o ARG, sets $opt_v, $opt_D, $opt_o
    getopts("vDo:");
    # -v, -D, -o ARG, sets $args{v}, $args{D}, $args{o}
    getopts("vDo:", \%args);

Or the standard Getopt::Long module to permit named arguments:

    use Getopt::Long;
    GetOptions( "verbose"  => \$verbose,        # --verbose
                "Debug"    => \$debug,          # --Debug
                "output=s" => \$output );       
            # --output=somestring or --output somestring

Another reason for preprocessing arguments is to make an empty argument list default to all files:

    @ARGV = glob("*") unless @ARGV;

You could even filter out all but plain, text files. This is a bit silent, of course, and you might prefer to mention them on the way.

    @ARGV = grep { -f && -T } @ARGV;

If you're using the -n or -p command-line options, you should put changes to @ARGV in a BEGIN{} block.

Remember that a normal open has special properties, in that it might call fopen(3S) or it might called popen(3S), depending on what its argument looks like; that's why it's sometimes called "magic open". Here's an example:

    $pwdinfo = `domainname` =~ /^(\(none\))?$/
                    ? '< /etc/passwd'
                    : 'ypcat passwd |';
    open(PWD, $pwdinfo)                 
                or die "can't open $pwdinfo: $!";

This sort of thing also comes into play in filter processing. Because <ARGV> processing employs the normal, shell-style Perl open, it respects all the special things we've already seen:

    $ myprogram f1 "cmd1|" - f2 "cmd2|" f3 < tmpfile

That program will read from the file f1, the process cmd1, standard input (tmpfile in this case), the f2 file, the cmd2 command, and finally the f3 file.

Yes, this also means that if you have files named "-" (and so on) in your directory, they won't be processed as literal files by open. You'll need to pass them as "./-", much as you would for the rm program, or you could use sysopen as described below.

One of the more interesting applications is to change files of a certain name into pipes. For example, to autoprocess gzipped or compressed files by decompressing them with gzip:

    @ARGV = map { /^\.(gz|Z)$/ ? "gzip -dc $_ |" : $_  } @ARGV;

Or, if you have the GET program installed from LWP, you can fetch URLs before processing them:

    @ARGV = map { m#^\w+://# ? "GET $_ |" : $_ } @ARGV;

It's not for nothing that this is called magic <ARGV>. Pretty nifty, eh?


Open à la C

If you want the convenience of the shell, then Perl's open is definitely the way to go. On the other hand, if you want finer precision than C's simplistic fopen(3S) provides you should look to Perl's sysopen, which is a direct hook into the open(2) system call. That does mean it's a bit more involved, but that's the price of precision.

sysopen takes 3 (or 4) arguments.

    sysopen HANDLE, PATH, FLAGS, [MASK]

The HANDLE argument is a filehandle just as with open. The PATH is a literal path, one that doesn't pay attention to any greater-thans or less-thans or pipes or minuses, nor ignore whitespace. If it's there, it's part of the path. The FLAGS argument contains one or more values derived from the Fcntl module that have been or'd together using the bitwise "|" operator. The final argument, the MASK, is optional; if present, it is combined with the user's current umask for the creation mode of the file. You should usually omit this.

Althou