# error-checking would, of course, be used open FILE, "filename"; # read open FILE, "< filename"; # read (explicit) open FILE, "> filename"; # overwrite open FILE, ">> filename"; # append open FILE, "+< filename"; # read and write open FILE, "+> filename"; # read and overwrite (clobber first) open FILE, "+>> filename"; # read and append open FILE, "program |"; # read from program open FILE, "| program"; # write to programFor safety's sake, the explicit forms should always be used, and with a space between the mode and the filename. Here's an example of why:
chomp(my $filename = <STDIN>); open FILE, $filename;This allows the user pass anything from "< /etc/passwd" to "rm -rf / |" to your open() call, neither of which you'd be too happy to permit. For the same reason, using open(F, ">$filename") isn't enough either -- the user could slip an extra > in on you and cause you to append, rather than overwrite.
# open FILEHANDLE, MODE, EXPR open FILE, "<", $filename; # read from $filenameIf you want to pipe to a program, the MODE should be "|-"; if you want to pipe from a program, the MODE should be "-|". In the case of call programs, you can send a list of arguments after the program name:
# open FILEHANDLE, MODE, EXPR, LIST open LS, "-|", "ls", "-R";That invokes ls with the -R switch (for recursive listing), and returns the output to Perl.
for my $f (@listing) {
open my($fh), "<", $f;
push @files, $fh;
}
open 0; # like: open 0, $0
open FORTUNE, "< /usr/share/games/fortunes/art";
{
local $/ = "\n%\n";
@fortunes = <FORTUNE>;
}
close FORTUNE;
This code makes use of the $/ variable -- the "input record separator"
-- to change how much each read of <FORTUNE> does. Instead of
stopping at "\n", it stops at "\n%\n" (the separator of my
computer's fortune files). This means that we can read multiple "lines" at
once. In fact, Perl has two special values of $/ explicitly for that
purpose:
while (read(FILE, $buf, 1024)) { ... }
# is like
{
local $/ = \1024;
while ($buf = <FILE>) { ... }
}
If you're wondering why I continually local()ize $/, it is
to make sure that the change to $/ are restricted to where we want it.
We don't want future filehandle-reads to be using the changed value.
open PASSWD, "/etc/passwd"
or die "can't read /etc/passwd: $!";
open MOD, "> /etc/weirdpasswd"
or die "can't write to /etc/weirdpasswd: $!";
$\ = $/; # ORS = IRS = "\n"
$, = ":"; # OFS = ","
while (<PASSWD>) {
chomp; # removes $/ from $_
my @f = split $,; # splits $_ on occurrences of $,
# fool around with @f
print MOD @f;
}
close MOD;
close PASSWD;
If we hadn't set $\ and $, in this code, the output file
would have been one long line of fields, with nothing in between each field,
and no way to separate one record from the next. However, since we have set
them, we automatically append $\ to each print() statement,
and automatically insert $, in between each argument to print().
Here's the explicit code that doesn't use these two variables:
while (<PASSWD>) {
chomp;
my @f = split ':';
# fool around with @f
print MOD join(':', @f), "\n";
}
While that may end up being more clear than the other, it's only that way
because you've not been exposed to the variables. I'm sure before you learned
how to use $_, your code was a lot more verbose; but once you embrace
that default variable, code like
for my $line (@lines) {
chomp $line;
my @fields = split /=/, $line;
for my $f (@fields) { $f =~ s/->/: /; }
# ...
}
became code like
for (@lines) {
chomp;
my @fields = split /=/;
for (@fields) { s/->/:/ }
# ...
}
It's the same with these other variables.
print "to stdout\n"; my $oldfh = select MOD; print "to mod\n"; select $oldfh; print "to stdout\n";Assuming you start out with STDOUT as your default output handle, the code runs as is described. The select() function (in the single argument form) takes a filehandle, sets it as the default, and returns the previously select()ed filehandle.
# turn on autoflushing for OUT
{
my $old = select OUT;
$| = 1;
select $old;
}
# another way, using IO::Handle
use IO::Handle;
autoflush OUT 1;
The IO::Handle module offers many helpful methods for filehandles
(which are internally objects of the IO::Handle class). You might
want to see what else it has to offer that you might want to use.
select((select(OUT), $|=1)[0]);The dissection of this code is as follows:
# alternate indenting and not indenting lines
for (@data) {
print " " x $|--;
print "$_\n";
}
This doesn't work with $|++... can you see why?
#!/usr/bin/perl -w
# inplace.pl ext code [files]
# ex: inplace.pl .bak '$_ = "" if /^#/' *.pl
use strict;
$^I = shift;
my $code = shift;
while (<>) {
eval $code;
print;
}
All the following symbols are strict-safe.
#!/usr/bin/perl -w
use strict;
my $ext = shift;
my $code = shift;
@ARGV = '-' unless @ARGV;
FILE:
while (defined($ARGV = shift)) {
my $backup;
# if we're not working with STDIN...
if ($ARGV ne '-') {
# get backup filename
if ($ext =~ /\*/) { ($backup = $ext) =~ s/\*/$ARGV/ }
else { $backup = "$ARGV$ext" }
# try renaming file
rename $ARGV => $backup or
warn("Can't rename $ARGV to $backup: $!, skipping file.") and
next FILE;
}
# with STDIN, there's no real backup done
else { $backup = '-' }
open ARGV, "< $backup" or
warn("Can't open $backup: $!") and
next FILE;
# if we're not dealing with STDIN,
# but $backup is $ARGV, we're doing real
# in-place editing, so we use a Unix trick:
# * open the file for reading
# * unlink it
# * open the file for writing
# this is a miracle, but it fails in DOS :(
if ($backup ne '-' and $backup eq $ARGV) {
unlink $backup or
warn("Can't remove $backup: $!, skipping file.") and
next FILE;
}
open ARGVOUT, "> $ARGV" or
warn("(panic) Can't write $ARGV: $!, skipping file.") and
next FILE;
while (<ARGV>) {
eval $code;
print ARGVOUT;
}
close ARGVOUT;
# note: we don't close ARGV!
}
Aren't you glad Perl does all that hard work for you?
while (<>) {
print "$ARGV ($.): $_";
}
If we have two files, a.txt and b.txt whose contents are
"abc\ndef\nghi\n" and "jkl\nmno\n" respectively, this program
outputs:
a.txt (1): abc a.txt (2): def a.txt (3): ghi b.txt (4): jkl b.txt (5): mnoNow, what if we want the line number to be reset for each new file? We need to be able to detect the end of the file. We can do that with the eof() function! There are two ways we can use the function for detecting the end of each input:
while (<>) {
print "$ARGV ($.): $_";
close ARGV if eof; # reset $.
}
# or
while (<>) {
print "$ARGV ($.): $_";
close ARGV if eof(ARGV); # reset $.
}
If you don't use any parentheses, and don't send an argument, Perl will check
the last filehandle read from. If you send an argument, it checks that
filehandle. "But japhy! What about eof()?" you ask? Well,
that's a very special case. If you want to know when you've reached the end of
all the input, you can use eof():
while (<>) {
print "$ARGV ($.): $_";
print "==end==\n" if eof(); # after ALL data
}
perl -ne 'print if /foo/' files
# becomes
perl -e 'while (<>) { print if /foo/ }' files
perl -pe 's/foo/bar/' files
# becomes
perl -e 'while (<>) { s/foo/bar/ } continue { print }' files
You can use -p with -i to write a simple one-liner file editor:
# keep backups perl -pi.bak -e 's/PERL/Perl/g' files # don't keep backups perl -pi -e 's/PERL/Perl/g' filesWhy do you think you have to say -pi -e, and can't use -pie?
Email comments to japhy@pobox.com