7.7. Writing a FilterProblem
You want to write a program that takes a list of filenames on the command line and reads from STDIN if no filenames were given. You'd like the user to be able to give the file
When you say: while (<>) { # ... } Perl translates this into:[ 4 ]
unshift(@ARGV, '-') unless @ARGV; while ($ARGV = shift @ARGV) { unless (open(ARGV, $ARGV)) { warn "Can't open $ARGV: $!\n"; next; } while (defined($_ = <ARGV>)) { # ... } }
You can access Behavior
If the user supplies no arguments, Perl sets
Next, the file processing loop removes one argument at a time from
The
You can change @ARGV = glob("*.[Cch]") unless @ARGV; Process options before the loop, either with one of the Getopt libraries described in Chapter 15, User Interfaces , or manually: # arg demo 1: Process optional -c flag if (@ARGV && $ARGV[0] eq '-c') { $chop_first++; shift; } # arg demo 2: Process optional -NUMBER flag if (@ARGV && $ARGV[0] =~ /^-(\d+)$/) { $columns = $1; shift; } # arg demo 3: Process clustering -a, -i, -n, or -u flags while (@ARGV && $ARGV[0] =~ /^-(.+)/ && (shift, ($_ = $1), 1)) { next if /^$/; s/a// && (++$append, redo); s/i// && (++$ignore_ints, redo); s/n// && (++$nostdout, redo); s/u// && (++$unbuffer, redo); die "usage: $0 [-ainu] [filenames] ...\n"; }
Other than its implicit looping over command-line arguments, undef $/; while (<>) { # $_ now has the complete contents of # the file whose name is in $ARGV }
If you localize { # create block for local local $/; # record separator now undef while (<>) { # do something; called functions still have # undeffed version of $/ } } # $/ restored here
Because processing while (<>) { print "$ARGV:$.:$_"; close ARGV if eof; }
Command-line optionsPerl has command-line options, -n , -p , and -i , to make writing filters and one-liners easier.
The
-n
option adds the Example 7.1: findlogin1#!/usr/bin/perl # findlogin1 - print all lines containing the string "login" while (<>) {# loop over files on command line print if /login/; } The program in Example 7.1 could be written as shown in Example 7.2 . Example 7.2: findlogin2#!/usr/bin/perl -n # findlogin2 - print all lines containing the string "login" print if /login/; You can combine the -n and -e options to run Perl code from the command line: % perl -ne 'print if /login/'
The
-p
option is like
-n
but it adds a Example 7.3: lowercase1#!/usr/bin/perl # lowercase - turn all lines into lowercase use locale; while (<>) { # loop over lines on command line s/([^\W0-9_])/\l$1/g; # change all letters to lowercase print; } The program in Example 7.3 could be written as shown in Example 7.4 . Example 7.4: lowercase2#!/usr/bin/perl -p # lowercase - turn all lines into lowercase use locale; s/([^\W0-9_])/\l$1/g;# change all letters to lowercase Or written from the command line as: % perl -Mlocale -pe 's/([^\W0-9_])/\l$1/g'
While using
-n
or
-p
for implicit input looping, the special label
Example 7.5: countchunks#!/usr/bin/perl -n # countchunks - count how many words are used. # skip comments, and bail on file if __END__ # or __DATA__ seen. for (split /\W+/) { next LINE if /^#/; close ARGV if /__(DATA|END)__/; $chunks++; } END { print "Found $chunks chunks\n" } The tcsh keeps a .history file in a format such that every other line contains a commented out timestamp in Epoch seconds: #+0894382237 less /etc/motd #+0894382239 vi ~/.exrc #+0894382242 date #+0894382242 who #+0894382288 telnet home A simple one-liner can render that legible: % perl -pe 's/^#\+(\d+)\n/localtime($1) . " "/e' The -i option changes each file on the command line. It is described in Recipe 7.9 , and is normally used in conjunction with -p . See Alsoperlrun (1), and the "Switches" section of Chapter 6 of Programming Perl ; Recipe 7.9 ; Recipe 16.6 |
|