develooper Front page | perl.perl5.porters | Postings from September 2003

RE: "How did I live without this?"

Thread Next
From:
scott.l.miller
Date:
September 5, 2003 09:51
Subject:
RE: "How did I live without this?"
Message ID:
1F7C0C8F4BD7C54A8BC55012FEF3DF6D013DBC44@omaexc11.americas.cpqcorp.net
Mark,

I took your trivial thing, and expanded upon it quite a bit.  This is certainly easier for me to use than the awk/cut etc tools...
I added the ability to grab multiple fields, ranges of fields etc.  One could also add the ability to change the field delimiter, but I didn't need that (yet).

#!/usr/bin/perl

use Getopt::Std;

getopts('ht');

usage() if $opt_h;
$field = shift or usage();
parse_field_list($field);

while (<>) {
   chomp;
   my @f = split;

   my $i=0;
   foreach $e (@nfl) {
      print " " if($i > 0); #keep from printing an extra space at the end
      print $f[$e];
      $i++;
      if($e =~ /rest/) { #want the rest of the fields printed
         foreach $k (($e+1) .. $#f) {
            print " ".$f[$k];
            $i++;
         }
      }
   }

   print "\n";
}

sub usage {
   print <<ENDUSAGE;
$0 field-number-list <fileglob>
   All files matching <fileglob> are used as input, the fields are delimited
   by spaces. The field-number-list must either be surrounded by quotes, or 
   have no whitespace within the list.  Commas separate the fields, the 
   fields may contain ranges denoted with '..', a range must have a 
   beginning but the end may be left off if the rest of the fields are 
   desired. Negative numbers indicate fields counting from the end of line.
   The fields are zero based, so if you want the first field, you
   must specify a zero.  Any non-numeric, comma, dash, and period characters
   will be silently removed from the field number list

   Output is sent to stdout

   Options: -h -t
      -h or no parameters will print this message.
      -t will print what the field list expanded into after parsing

   examples:
   $0 1..3,5,-3.. filename
       This will print the 2nd, 3rd and 4th fields, the 6th, and the last 3 fields.

   $0 5..0 filename
       This will print the first 6 fields in reverse order.

ENDUSAGE
exit 1;
}

sub parse_field_list {
   $_=$_[0];
   tr/0-9,.\-//c;          #remove any illegal characters from field list
   tr/,/,/s;               #remove excess commas from field list
   local @fl = split /,/;  #split the field list

   my ($e);
   foreach $e (@fl) {
      next if ($e eq undef);  #if empty field, ignore it

      if ($e =~ /\.\./) { #if user specified a range
         my ($rb,$re);
         ($rb,$re) = split /\.\./, $e;  #grab beginning and ending

         if ($rb eq undef) {
            die "range operator missing initial argument\n";
         }

         if ($re eq undef) {  #if no ending argument given for range
            if($rb < 0) {     #if beginning is negative, the last field is -1
               $re = -1;
            } else { 
               #let the main routine deal with printing the rest of the fields
               push @nfl, $rb."rest";
               next;
            }
         }

         if ($rb <= $re) { #if range is to be reversed, handle that
            push @nfl, ($rb .. $re);
         } else {
            push @nfl, reverse ($re .. $rb);
         }
      } else {
         push @nfl, $e;
      }
   }

   if ($opt_t) {
      foreach $e (@nfl) {
         print "$e, ";
      }
      print "\n";
   }
}

-----Original Message-----
From: Mark Jason Dominus [mailto:mjd@plover.com]
Sent: Thursday, August 14, 2003 12:13 PM
To: perl5-porters@perl.org
Subject: "How did I live without this?"



This is totally trivial, but it's making my life a lot easier.  It's a
command that I've named 'f' for 'field':

        #!/usr/bin/perl

        my $field = shift or usage();
        $field -= 1 if $field > 0;

        while (<>) {
                chomp;
                my @f = split;
                print $f[$field], "\n";
        }

        sub usage {
                print "$0 fieldnumber\n"; 
                exit 1;
        }


Now when I want to extract field #6 (the target URL) from my web log
file, I can use

        f 6 /usr/local/apache/logs/perl-access-log

Sometimes I want to run some command on all the files in the current
directory that were modified today:

        command `ls -l | grep 'Aug 14' | f -1`

The 'f -1' extracts the last field (the filename) from each line.

I do know how I got along without this.  I used to use 'awk':

        awk '{print $6}' /usr/local/apache/logs/perl-access-log

'f' is a lot less to type.

Now that I'm composing this, it occurs to me that I could save a bit
of code by rewriting this as

        #!/usr/bin/perl -aln

        sub usage {
                print STDERR "$0 fieldnumber"; 
                exit 1;
        }

        BEGIN {
          $field = shift or usage();
          $field -= 1 if $field > 0;
        }

        print $F[$field];

and let '-a' take care of the 'split', '-l' take care of the 'chomp',
and '-n' take care of the 'while'.  I'm not sure I like it better, though.


Thread Next


nntp.perl.org: Perl Programming lists via nntp and http.
Comments to Ask Bjørn Hansen at ask@perl.org | Group listing | About