Command-Line Options
#!/usr/bin/perl -w

use Getopt::Std;

#  Duplicate grep with -l, -v, and -n the allowable options.  Die if
#  an option is given which does not exist!  Getopts provides error
#  message which is sent to STDERR.  Options can be after one hyphen
#  (e.g. -vn) or separately (e.g. -v -n).

die "\n" if !getopts("lvn", \%opts);   

#  The options now will NOT be considered part of the @ARGV array.
die "Usage: $0 [-lvn] \"pattern\" file1 ...\n" if @ARGV < 2;
die "Incompatible options!\n" if $opts{'l'} && $opts{'n'};

$pattern = shift @ARGV;  #  Get pattern for our own grep.

foreach $file (@ARGV)
{
      if (!open(F, "$file"))
      {
           print "Could not open $file!\n";
           next;
      }
      if (!-T "$file")
      {
           print "Skipping $file -- not a text file!\n";
           next;
      }

      if ($opts{'l'})
      {
            @matches = grep {/$pattern/} <F>  if !$opts{'v'};
            @matches = grep {!/$pattern/} <F> if  $opts{'v'};
            print "$file\n" if @matches;
      }
      elsif ($opts{'n'})
      {
            $matches = 0;
            while (<F>)
            {
                 if ($opts{'v'} ? !/$pattern/ : /$pattern/)
                 {
                       $matches++;
               print "$file\n", "-" x length($file), "\n" if $matches == 1;
                       printf "%-10s: %-s", $., $_ ;
                 }
            }
      }
      else  #  No options at all or just $opt_v.
      {
            @matches = grep {!/$pattern/} <F> if $opts{'v'};
            @matches = grep {/$pattern/} <F>  if !$opts{'v'};
            next if !@matches;
            print "$file\n", "-" x length($file), "\n";
            print join("", @matches), "\n";
      }
      close F;   #  Reset $.  --  not done without explicit close.
}
##########################  Sample Sessions with Script  #####################
 
$ mygrep.pl getopts getopts.pl database.demo.8     # "Getopts" is pattern.
getopts.pl
----------
getopts("lvn", \%opts);

$ mygrep.pl foobar *      #  "Foobar" is pattern.
Skipping emp.db -- not a text file!
Skipping events.db -- not a text file!

grep-map.demo.2
---------------
@strs = qw(foobar simple four on eighteen);

Skipping homers.db -- not a text file!
Skipping objects -- not a text file!

signals.demo.11
---------------
foobar
Output: foobar
foobar
Output: foobar

sort.demo.5
-----------
@list = qw(goofy foobar xxxxxx john perl);
Sum for foobar: 633
foobar

$ mygrep.pl -n foobar *
Skipping emp.db -- not a text file!
Skipping events.db -- not a text file!

grep-map.demo.2
---------------
29        : @strs = qw(foobar simple four on eighteen);

Skipping homers.db -- not a text file!
Skipping objects -- not a text file!

signals.demo.11
---------------
135       : foobar
136       : Output: foobar
158       : foobar
159       : Output: foobar
sort.demo.5
-----------
78        : @list = qw(goofy foobar xxxxxx john perl);
180       : Sum for foobar: 633
188       : foobar

$ mygrep.pl -vln foobar *
Incompatible options!

$ mygrep.pl -c mygrep mygrep.pl
Unknown option: c


##################  Command Line Options with Arguments  ##################
#!/usr/bin/perl -w

use Getopt::Std;

#  Duplicate head and tail in one utility.  Option -s means "start at line"
#  and then the line number.  Option -h means "head".  Option -t means
#  "tail".  Unfortunately, there are no defaults if you specify a : in
#   the option string.  Here the options mutually exclusive.

die "\n" if !getopts("s:h:t:", \%opts);   
die "Usage: $0 [-s arg -h arg -t arg] file\n" if @ARGV != 1;
open(F, "$ARGV[0]") or die "Cannot open $ARGV[0]!\n";

#  Bail if more than one argument present or if none are present.
#  If no hash reference is given to the getopts function, then the
#  option arguments are held in $opts{t}, $opts{h}, and $opts{s} in this
#  program.  If you do give a hash reference, USE THE HASH!!

@numopts = keys %opts;
die "May only use one option!\n" if @numopts > 1;
die "Must use one option!\n" if @numopts == 0;

$line = $opts{$numopts[0]};  #  Only one option, must be in [0] index!
die "Illegal option argument!\n" if $line =~ /\D/ || $line == 0;

#  See how many lines we have for -t option.  Then rewind file, reset $.
@lines = <F>;
die "Illegal linenumber!\n" if $line > @lines;

seek(F, 0, 0);
$. = 0;     # Necessary!!

while (<F>)
{
      if ($opts{s})  #  If $opts{s} is set it will have a linenumber in it. 
      {
            printf "%-5d: %-s", $., $_ if $. >= $line; 
      }
      if ($opts{h})
      {
            printf "%-5d: %-s", $., $_ if $. <= $line; 
            last if $. > $line;  #  Why do more work than we need to?
      }
      if ($opts{t})
      {
            printf "%-5d: %-s", $., $_ if $. > @lines - $line; 
      }
}



##########################  Program Sessions Below  #########################

$ display.pl getopts2.pl
Must use one option!

$ display.pl -h 10 -s 10 getopts2.pl
May only use one option!

$ display.pl -s getopts2.pl    #  Proof that option MUST have an argument!
Usage: ./display.pl [-s arg -h arg -t arg] file

$ display.pl -h3 getopts2.pl   #  Note that arg can run into option string!
1    : #!/usr/bin/perl -w
2    : 
3    : use Getopt::Std;

$ display.pl -t 4 getopts2.pl
42   :       {
43   :             printf "%-5d: %-s", $., $_ if $. > @lines - $line; 
44   :       }
45   : }

$ display.pl -s 40 getopts2.pl
40   :       }
41   :       if ($opt_t)
42   :       {
43   :             printf "%-5d: %-s", $., $_ if $. > @lines - $line; 
44   :       }
45   : }
$

#  Notes on using options with and without arguments:
#      1)  If your getopts line says that an option has an argument,
#          the argument MUST be there.  No defaults!!
#
#      2)  If an option has an argument, then its value will be in the
#          hash given to getopts.  Example, if you have a -X option
#          which takes an argument and your options hash is %opts,
#          then $opts{'X'} will contain the argument value if -X is
#          on the user's command line.
#         
#      3)  If an option, say the -X above, does NOT have an argument,
#          then $opts{'X'} will be 1 if -X is present and 0 if not.
#
#      4)  Getopts removes all options and option arguments from @ARGV!
#  
#      5)  When using getopts, use die "\n" because getopts itself will
#          provide all necessary error messages.