10.2. Passing Arguments
10.2.1. The ARGV Array
How does Perl pass command-line arguments to a Perl script? If you are coming from a C, awk, or C shell background, at first glance you might think, "Oh, I already know this!" Beware! There are some subtle differences. So again, read on.
Perl does store arguments in a special array called ARGV. The subscript starts at zero and, unlike C and awk, ARGV[0] does not represent the name of the program; it represents the name of the first word after the script name. Like the shell languages, the $0 special variable is used to hold the name of the Perl script. Unlike the C shell, the $#ARGV variable contains the number of the last subscript in the array, not the number of elements in the array. The number of arguments is $#ARGV + 1. $#ARGV initially has a value of –1.
When ARGV, the filehandle, is enclosed in angle brackets, <ARGV>, the command-line argument is treated as a filename. The filename is assigned to ARGV and the @ARGV array is shifted immediately to the left by one, thereby shortening the @ARGV array.
The value that is shifted off the @ARGV array is assigned to $ARGV. $ARGV contains the name of the currently selected filehandle.

Example 10.27.
(The Script)
#!/usr/bin/perl
1 die "$0 requires an argument.\n" if $#ARGV < 0 ;
# Must have at least one argument
2 print "@ARGV\n"; # Print all arguments
3 print "$ARGV[0]\n"; # Print first argument
4 print "$ARGV[1]\n"; # Print second argument
5 print "There are ", $#ARGV + 1," arguments.\n";
# $#ARGV is the last subscript
6 print "$ARGV[$#ARGV] is the last one.\n"; # Print last arg
(Output)
$ perl.arg
2 perl.arg requires an argument.
$ perl.arg f1 f2 f3 f4 f5
2 f1 f2 f3 f4 f5
3 f1
4 f2
5 There are 5 arguments.
6 f5 is the last one.
If there are no command-line arguments, the die function is executed and the script is terminated. The $0 special variable holds the name of the Perl script, perl.arg. The contents of the @ARGV array are printed. The first argument, not the script name, is printed. The second argument is printed. The $#ARGV variable contains the number value of the last subscript. Since the subscript starts at zero, $#ARGV + 1 is the total number of arguments, not counting the script name. Since $#ARGV contains the value of the last subscript, $ARGV[$#ARGV] is the value of the last element of the @ARGV array.
|
10.2.2. ARGV and the Null Filehandle
When used in loop expressions and enclosed in the input angle brackets (<>), each element of the ARGV array is treated as a special filehandle. Perl shifts through the array, storing each element of the array in a variable $ARGV. A set of empty angle brackets is called the null filehandle, and Perl implicitly uses each element of the ARGV array as a filehandle. When using the input operators <>, either with or without the keyword ARGV, Perl shifts through its arguments one at a time, allowing you to process each argument in turn. Once the ARGV filehandle has been opened, the arguments are shifted off one at a time, so if they are to be used later, they must be saved in another array.
Example 10.28.
(The Text Files)
$ cat f1
Hello there. Nice day.
$ cat f2
Are you sure about that?
$ cat f3
This is it.
This is the end.
(The Script)
1 while( <ARGV> ) {print ;}
2 print "The value of \$ARGV[0] is $ARGV[0].\n";
(Output)
$ argv.test f1 f2 f3
Hello there. Nice day.
Are you sure about that?
This is it.
This is the end.
The value of $ARGV[0] is .
This will print the contents of all the files named at the command line. Once used, the argument is shifted off. The contents of f1, f2, and f3 are read and then printed, respectively. Since the arguments were all shifted off, $ARGV[0] has no value and, therefore, nothing is printed.

|
Example 10.29.
Code View: (The Text File: emp.names)
Steve Blenheim
Betty Boop
Igor Chevsky
Norma Cord
Jon DeLoach
Karen Evich
(The Script)
# Scriptname: grab.pl
# Program will behave like grep -- will search for a pattern
# in any number of files.
1 if ( ($#ARGV < 1 ) {die "Usage: $0 pattern filename(s) \n";}
2 $pattern = shift;
3 while($line=<ARGV>){
print "$ARGV: $.: $line" if $line =~ /$pattern/i;
close(ARGV) if eof;
}
(Output)
$ grab.pl
1 Usage: grab.pl pattern filenames(s)
$ grab.pl 'norma' db
2 db:5: Norma Cord
$ grab.pl 'Sir Lancelot' db
3 db:4: Sir Lancelot
$ grab.pl '^.... ' db
4 db:3: Lori Gortz
$ grab.pl Steve d*
5 datebook.master:12: Johann Erickson:Stevensville, Montana
datafile:8: Steven Daniels:496-456-5676:83755:11/12/56:20300
db:1: Steve Blenheim
If there are no command-line arguments, the die function is executed. The first argument is shifted from the @ARGV array. This should be the pattern that will be searched for. Since the first argument was shifted off the @ARGV array and assigned to the scalar $pattern, the remaining arguments passed in from the command line are assigned in turn to the ARGV filehandle. When the while loop is entered, a line is read and assigned to $line. The $ARGV scalar holds the name of the file that is currently being processed. The $. variable holds the current line number. If the value in $pattern is matched, the filename where it was found, the number of the line where the pattern was found, and the line itself are printed. The i after the last delimiter in the pattern turns off case sensitivity. When the file being processed reaches the end of file (EOF), the ARGV filehandle is closed. This causes the $. variable to be reset. If ARGV is not closed explicitly here, the $. variable will continue to increment and not be set back to 1 when the next file is read.
|
Example 10.30.
Code View: (The Script)
1 unless ( $#ARGV == 0 ){ die "Usage: $0 <argument>: $!"; }
2 open(PASSWD, "etc/passwd") || die "Can't open: $!";
3 $username=shift(@ARGV);
4 while( $pwline = <PASSWD>){
5 unless ( $pwline =~ /$username:/){ die "$username is not
a user here.\n";}
}
6 close PASSWD;
7 open(LOGGEDON, "who |" ) || die "Can't open: $!" ;
8 while($logged = <LOGGEDON> ){
if ( $logged =~ /$username/){ $logged_on = 1; last;}
}
9 close LOGGEDON;
die "$username is not logged on.\n" if ! $logged_on;
print "$username is logged on and running these processes.\n";
10 open(PROC, "ps -aux|" ) || die "Can't open: $! ";
while($line=<PROC>){
print "$line" if $line =~ /$username:/;
}
11 close PROC;
print '*' x 80; "\n";
print "So long.\n";
(Output)
$ checkon
1 Usage: checkon <argument>: at checkon line 6.
$ checkon joe
5 Joe is not a user here.
$ checkon ellie
8 ellie is logged on and running these processes:
ellie 3825 6.4 4.5 212 464 p5 R 12:18 0:00 ps -aux
ellie 1383 0.8 8.4 360 876 p4 S Dec 26 11:34 /usr/local/OW3/bin/xview
ellie 173 0.8 13.4 1932 1392 co S Dec 20389:19 /usr/local/OW3/bin/xnews
ellie 164 0.0 0.0 100 0 co IW Dec 20 0:00 -c
< some of the output was cut to save space >
ellie 3822 0.0 0.0 0 0 p5 Z Dec 20 0:00 <defunct>
ellie 3823 0.0 1.1 28 112 p5 S 12:18 0:00 sh -c ps -aux | grep '^'
ellie 3821 0.0 5.6 144 580 p5 S 12:18 0:00 /bin/perl checkon ellie
ellie 3824 0.0 1.8 32 192 p5 S 12:18 0:00 grep ^ellie
ellie 3815 0.0 1.9 24 196 p4 S 12:18 0:00 script checkon.tsc
******************************************************************************
This script calls for only one argument. If ARGV is empty (i.e., no arguments are passed at the command line), the die function is executed and the script exits with an error message. (Remember: $#ARGV is the number of the last subscript in the ARGV array, and ARGV[0] is the first argument, not counting the name of the script, which is $0.) If more than one argument is passed, the script will also exit with the error message. The /etc/passwd file is opened for reading via the PASSWD filehandle. The first argument is shifted from @ARGV and assigned to $username. Each time the while loop is entered, a line of the /etc/passwd file is read via the PASSWD filehandle. The =~ is used to test if the first argument passed matches the $username. If a match is not found, the loop is exited. The filehandle is closed. The filehandle LOGGEDON is opened as an input filter. Output from the UNIX who command will be piped to the filehandle. Each line of the input filter is tested. If the user is logged on, the scalar $logged_on is set to 1, and the loop is exited. The input filter is closed. The filehandle PROC is opened as an input filter. Output from the UNIX ps command will be piped to the filehandle. Each line from the filter is read in turn and placed in the scalar $line. If $line contains a match for the user, that line will be printed to STDOUT, the screen.
|
10.2.3. The eof Function
The eof function can be used to test if end of file has been reached. It returns 1 if either the next read operation on a FILEHANDLE is at the end of the file or the file was not opened. Without an argument, the eof function returns the eof status of the last file read. The eof function with parentheses can be used in a loop block to test the end of file when the last filehandle has been read. Without parentheses, each file opened can be tested for end of file.
eof(FILEHANDLE)
eof()
eof
Example 10.31.
(The Text File: emp.names)
Steve Blenheim
Betty Boop
Igor Chevsky
Norma Cord
Jon DeLoach
Karen Evich
(In Script)
1 open ( DB, "emp.names") || die "Can't open emp.names: $!";
2 while(<DB>){
3 print if (/Norma/ .. eof); # .. is the range operator
}
(Output)
Norma Cord
Jonathan DeLoach
Karen Evitch
The file emp.names is opened via the DB filehandle. The while loop reads a line at a time from the filehandle DB. When the line containing the regular expression Norma is reached, that line and all lines in the range from Norma until eof (the end of file) are printed.
|
|
Example 10.32.
Code View: (The Text Files)
$ cat file1
abc
def
ghi
$ cat file2
1234
5678
9101112
(The Script)
#!/usr/bin/perl
# eof.p script
1 while(<>){
2 print "$.\t$_";
3 if (eof){
print "-" x 30, "\n";
4 close(ARGV);
}
}
(Output)
$ eof.p file1 file2
1 abc
2 def
3 ghi
---------------------------
1 1234
2 5678
3 9101112
---------------------------
The first argument stored in the ARGV array is file1. The null filehandle is used in the while expression. The file file1 is opened for reading. The $. variable is a special variable containing the line number of the currently opened filehandle. It is printed, followed by a tab and then the line itself. If end of file is reached, print a row of 30 dashes. The filehandle is closed in order to reset the $. value back to 1 for the next file that is opened. When file1 reaches end of file, the next argument, file2, is processed, starting at line 1.
|
10.2.4. The -i Switch—Editing Files in Place
The -i option is used to edit files in place. The files are named at the command line and stored in the @ARGV array. Perl will automatically rename the output file to the same name as the input file. The output file will be the selected default file for printing. To ensure that you keep a backup of the original file, you can specify an extension to the -i flag, such as -i.bak. The original file will be renamed filename.bak. The file must be assigned to the ARGV filehandle when it is being read from. Multiple files can be passed in from the command line and each in turn will be edited in place.
Example 10.33.
(The Text File)
1 $ more names
igor chevsky
norma corder
jennifer cowan
john deloach
fred fardbarkle
lori gortz
paco gutierrez
ephram hardy
james ikeda
(The Script)
2 #!/usr/bin/perl –i.bak
# Scriptname: inplace
3 while(<ARGV>){ # Open ARGV for reading
4 tr/a-z/A-Z/;
5 print; # Output goes to file currently being read in-place
6 close ARGV if eof;
}
(Output)
7 $ inplace names
$ more names
IGOR CHEVSKY
NORMA CORDER
JENNIFER COWAN
JOHN DELOACH
FRED FARDBARKLE
LORI GORTZ
PACO GUTIERREZ
EPHRAM HARDY
JAMES IKEDA
8 $ more names.bak
igor chevsky
norma corder
jennifer cowan
john deloach
fred fardbarkle
lori gortz
paco gutierrez
ephram hardy
james ikeda
The contents of the original text file, called names, is printed. The -i in-place switch is used with an extension. The names file will be edited in place and the original file will be saved in names.bak. The while loop is entered. The ARGV filehandle will be opened for reading. All lowercase letters are translated to uppercase letters in the file being processed (tr function). The print function sends its output to the file being processed in place. The ARGV filehandle will be closed when the end of file is reached. This makes it possible to reset line numbering for each file when processing multiple files or to mark the end of files when appending. The names file has been changed, illustrating that the file was modified in place. The names.bak file was created as a backup file for the original file. The original file has been changed.
|