Contents
5. Taste of Perl
Quite useful Perl programs can be short. Suppose we want to change the same
text in many files. Instead of editing each possible file or constructing some
cryptic find, awk, or sed commands, you could issue a single command:
Example: Amazing Perl One-Liner That Substitutes Text In Multiple
Files
perl -e 's/gopher/World Wide Web/gi' -p -i.bak *.html
This command, issued at the Unix prompt, executes the short Perl program
specified in single quotes. This program consists of one perl operation; it
substitutes for original word "gopher" the phrase "World Wide Web", (globally,
ignoring case). The re mainder of the Unix command indicates that the perl
program should run for each file ending in ".html" in the current directory. If
any file "blah.html" needs changing, a backup of the original is made as file
"blah.html.bak". Programming Perl lists additional handy
one-liners.
For those accustomed to "classic" procedural programming, the "amazing
one-liner" above can be expanded in Perl in a style more like C or Pascal:
Example: Global Substitution, The Scenic Route
#!/usr/local/bin/perl -w
# File: go2www
# This Perl program in classic programming style changes
# the string "gopher" to "World Wide Web" in all files
# specified on the command line.
# 19950926 gkj
$original='gopher';
$replacement="World Wide Web";
$nchanges = 0;
# The input record separator is defined by Perl global
# variable $/. It can be anything, including multiple
# characters. Normally it is "\n", newline. Here, we
# say there is no record separator, so the whole file
# is read as one long record, newlines included.
undef $/;
# Suppose this program was invoked with the command
# go2www ax.html big.basket.html candle.html
# Then builtin list @ARGV would contain three elments
# ('ax.html', 'big.basket.html', 'candle.html')
# These could be accessed as $ARGV[0] $ARGV[1] $ARGV[2]
foreach $file (@ARGV) {
if (! open(INPUT,"<$file") ) {
print STDERR "Can't open input file $bakfile\n";
next;
}
# Read input file as one long record.
$data=<INPUT>;
close INPUT;
if ($data =~ s/$original/$replacement/gi) {
$bakfile = "$file.bak";
# Abort if can't backup original or output.
if (! rename($file,$bakfile)) {
die "Can't rename $file $!";
}
if (! open(OUTPUT,">$file") ) {
die "Can't open output file $file\n";
}
print OUTPUT $data;
close OUTPUT;
print STDERR "$file changed\n";
$nchanges++;
}
else { print STDERR "$file not changed\n"; }
}
print STDERR "$nchanges files changed.\n";
exit(0);
Questions:
- What do you guess that the "!" means, as in:
if (! open(OUTPUT,">$file") ) {
die "Can't open output file $file\n";
}
- What does the ">" probably mean here? Compare with "open(INPUT ...)".
- What does "die" do?
- Some languages use "IF ... THEN DO ... END; ELSE IF ... THEN DO ... END".
How is this notated in Perl?
- What does $nchanges++ do?
The Perl Creed is, "There is more than one way!" This noble freedom of
expression however results in the first of the four Perl
Paradoxes: Perl programs are easy to write but not always easy to
read. For example, the following lines are equivalent!
if ($x == 0) {$y = 10;} else {$y = 20;}
$y = $x==0 ? 10 : 20;
$y = 20; $y = 10 if $x==0;
unless ($x == 0) {$y=20} else {$y=10}
if ($x) {$y=20} else {$y=10}
$y = (10,20)[$x != 0];