tphyahoo has asked for the wisdom of the Perl Monks concerning the following question:

I'm acclimatizing to the "unix" way, and have found some situations where it's useful to process output of another command with pipes, through a custom perl script. Now, invoking the script with "perl -an" is often useful, I like it because it behaves in an intuitive way when dealing with pipes.

Now I have some little utilities built on "perl -an" that I would like to make more powerful by allowing flags to specify certain types of behavior. A simple example follows, where I'd like to select either letters or numbers from my piped output. Here, it doesn't work because I guess perl -an wants another file where I am trying to give it a flag.

Is there some way to do what I want with "perl -an" type invocation? Or do I have to use the diamond operator on STDOUT, like I was used to doing before I discovered perl -an?

Thanks... :)

$cat input.txt | ./anWithArgs.pl letters # should spit out lines with +a letter Can't open letters: Datei oder Verzeichnis nicht gefunden. $cat input.txt | ./anWithArgs.pl numbers # should spit out lines with +a number Can't open numbers: Datei oder Verzeichnis nicht gefunden. $cat anWithArgs.pl #!/usr/bin/perl -an my $type = shift or die "no type"; if ( $type eq 'letters' ) { print if $_ =~ /\w/; } elsif ( $type eq 'numbers' ) { print if $_ =~ /\d/; } else { print "bad type: $type" } $cat input.txt a b c d e f g 1 2 3 4 5 6 7 8 9

Replies are listed 'Best First'.
Re: custom args with perl -an type invocation?
by Tanktalus (Canon) on Jun 27, 2006 at 15:05 UTC

    Step 1. Your code, because of the -n, is all wrapped up in a loop. Which means that your "my $type = shift" statement will be executed each time through the loop. It also means that your $type is lexical to each time through the loop. Changing that line to:

    our $type; BEGIN { $type ||= shift or die "no type"; }
    solves that by creating $type as a global, and only setting it once (using ||=), and doing it before the loop (using BEGIN).

    Step 2: fixing the regex. \w is "word" characters, which includes letters and numbers and the _. You probably want [[:alpha:]]. In which case, the total program looks like this:

    #!/usr/bin/perl -an our $type; BEGIN { $type ||= shift or die "no type"; } if ( $type eq 'letters' ) { print if $_ =~ /[[:alpha:]]/; } elsif ( $type eq 'numbers' ) { print if $_ =~ /\d/; } else { print "bad type: $type" }
    Tested. Hope that helps.

    PS: ++ - that's really a cool use for perl, too ;-)

Re: custom args with perl -an type invocation?
by derby (Abbot) on Jun 27, 2006 at 15:37 UTC

    Although I like perl's command line switches for one-liners, I question their value inside of scripts (maybe my concept of best practice is wonky). The only one I ever really used was -w but I've given that up for use warnings;. For the example given, I don't see any need for the -a and you can avoid the -n by just coding explicity:

    #!/usr/bin/perl our $type ||= shift or die "no type"; while( <> ) { if ( $type eq 'letters' ) { print if $_ =~ /[[:alpha:]]/; } elsif ( $type eq 'numbers' ) { print if $_ =~ /\d/; } else { print "bad type: $type" } }
    While the -an and the BEGIN blocks highlight TIMTOWTDI, I find the explicit looping makes the script easier/quicker to understand and will be better suited if/when you shift to argument parsing modules.

    -derby
      I would also change
      print "bad type: $type"
      to
      die "bad type: $type"
      just so you don't have to endure output like
      bad type: letterbad type: letterbad type: letterbad type: letterbad ty +pe: letterbad type: letterbad type: letterbad type: letterbad type: l +etterbad type: letterbad type: letterbad type: letterbad type: letter +bad type: letterbad type: letterbad type: letterbad type: letterbad t +ype: letterbad type: letterbad type: letterbad type: letterbad type: +letterbad type: letterbad type: letterbad type: letterbad type: lette +rbad type: letterbad type: letterbad type: letterbad type: letterbad +type: letterbad type: letter$
      Here, I think you can just say $type = shift, don't need to say $type ||= shift
Re: custom args with perl -an type invocation?
by starbolin (Hermit) on Jun 27, 2006 at 19:04 UTC

    The cat is rarely necessary. Using the code Tanktalus posted:

    anWithArgs.pl numbers < input.txt

    Makes it easier to rerun with different files.

    s//----->\t/;$~="JAPH";s//\r<$~~/;{s|~$~-|-~$~|||s |-$~~|$~~-|||s,<$~~,<~$~,,s,~$~>,$~~>,, $|=1,select$,,$,,$,,1e-1;print;redo}
Re: custom args with perl -an type invocation?
by jwkrahn (Abbot) on Jun 27, 2006 at 22:55 UTC
    The -a switch splits the current line on whitespace and puts the results in the @F array but you are not using the @F array in your code so you probably don't need the -a switch. A simple method of providing switches to your program is Perl's -s switch (described in the perlrun document.)
    # With Perl's -s switch $ ./anWithArgs.pl -type=letters input.txt $ cat anWithArgs.pl #!/usr/bin/perl -sn if ( $type eq 'letters' ) { print if /[[:alpha:]]/; } elsif ( $type eq 'numbers' ) { print if /\d/; } else { print "bad type: $type" }
      Thank you. -s is exactly what I was looking for.