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

I'm going through The Learning Perl book from O'Reilly, and they use this in one of the solutions of an exercise:

push @numbers, split while <>;

They use that to read they list of arguments from the command line. however, when I actually run it, I get an error as expected stating that it can't ready the files, with <> treating the arguments as files, not input. I hope this makes sense. I'm wondering how this would be properly written?

Replies are listed 'Best First'.
Re: <> to read command line options?
by kcott (Archbishop) on Aug 16, 2013 at 02:51 UTC

    G'day slackcub,

    Welcome to the monastery.

    I don't own that book; however, for those that do, it would have been helpful to identify where in the book that one line of code exists.

    I expect that the book is talking about reading the contents of files whose filenames occur on the command-line. Given this file:

    $ cat pm_1049671_temp.txt a b c d e f g h i

    Here's what I think the book is trying to explain:

    $ perl -Mstrict -Mwarnings -le ' my @numbers; push @numbers, split while <>; print "@numbers"; ' pm_1049671_temp.txt a b c d e f g h i

    You can have more than one filename on the command-line. Here's another file:

    $ cat pm_1049671_2temp.txt j k l m n o p q r

    Here's the same code reading the contents of both files:

    $ perl -Mstrict -Mwarnings -le ' my @numbers; push @numbers, split while <>; print "@numbers"; ' pm_1049671_temp.txt pm_1049671_2temp.txt a b c d e f g h i j k l m n o p q r

    The actual command-line arguments are available to your program in the @ARGV array:

    $ perl -Mstrict -Mwarnings -le ' print "@ARGV"; ' pm_1049671_temp.txt pm_1049671_2temp.txt pm_1049671_temp.txt pm_1049671_2temp.txt

    Here's some further reading that may be helpful to you:

    • perlop: I/O Operators — I suggest you read the introductory paragraphs (for background information) and then, a little further down, you'll find "The null filehandle <> is special: ..." which relates directly to your current issue.
    • perlvar: Variables related to filehandles — Again, read the introductory information then, further down, find $ARGV, @ARGV, ARGV and ARGVOUT; $. (a little after those) also has relevant information.
    • perlrun — This has useful information regarding command-line usage; search for "<>" and "ARGV" to find items related to your question. There's information about other ways to deal with filenames on the command-line: I'll leave you to reseach this yourself.

    -- Ken

Re: <> to read command line options?
by 2teez (Vicar) on Aug 16, 2013 at 08:21 UTC

    slackcub wrote:..They use that to read they list of arguments **from the command line...

    That is not SO! Sir!! :). I suppose you presumed that.
    Reading from the explanation of solution given by the author(s) of the book on that exercise:

    That second line of code is too confusing *(that is referring to push @numbers, split while <>;), isn’t it? Well, we did that on purpose. Although we recommend that you write clear code, some people like writing code that’s as hard to understand as possible, so we want you to be prepared for the worst. Someday, you’ll need to maintain confusing code like this.
    Since that line uses the while modifier, it’s the same as if it were written in a loop like this:
    while (<>) { push @numbers, split; }
    That’s better, but maybe it’s still a little unclear. (Nevertheless, we don’t have a quibble about writing it this way. This one is on the correct side of the “too hard to understand at a glance” line.) The while loop **is reading the input one line at a time (from the user’s choice of input sources, as shown by the diamond operator), and split is, by default, splitting that on whitespace to make a list of words—or in this case, a list of numbers. The input is just a stream of numbers separated by whitespace, after all. Either way you write it, then, that while loop will put all of the numbers from the input into @numbers.


    NOTE:
    * included by me for clarity.
    ** Bold text were used for emphasis.

    If you tell me, I'll forget.
    If you show me, I'll remember.
    if you involve me, I'll understand.
    --- Author unknown to me
Re: <> to read command line options?
by Anonymous Monk on Aug 16, 2013 at 02:20 UTC

      oh, I figured they should have used @ARGV, but this is a quote from them in the answer:

      my @numbers;
      push @numbers, split while <>;
      foreach (sort { $a <=>$b } @numbers) {
      printf "%20g\n", $_;
      }

      then later in the explanation of the answer:

      The while loop is reading the input one line at a time (from the user’s choice of input sources, as shown by the diamond operator), and split is, by default, splitting that on whitespace to make a list of words—or in this case, a list of numbers. The input is just a stream of numbers separated by whitespace, after all. Either way you write it, then, that while loop will put all of the numbers from the input into @numbers.

      The instructions in the exercise are:

      Write a program to read in a list of numbers and sort them numerically +, printing out the resulting list in a right-justified column. Try it + out on this sample data: 17 1000 04 1.50 3.14159 –10 1.5 4 2001 90210 666

      unless I'm reading it wrong and they actually wanted it in a file. I was assuming it was supposed to be as command line options. That's certainly possible, as I'm still new to all this! ;}

        Yes, it does want the numbers as a file, rather than as command line arguments. If you make a numbers.txt file with all the numbers, then use numbers.txt as the only argument to your program, it does what it's supposed to.

        The diamond operator <> lets you choose what to use for input in the arguments to your program. When you leave it empty, it will wait for user input. With a program like this one, it doesn't tell you that it's waiting for something, which was surprising when I did this exercise myself a couple of months ago :)

Re: <> to read command line options?
by ww (Archbishop) on Aug 16, 2013 at 02:34 UTC

    Can you give us a reference (page, edition) for the code you showed (without using <c>...code here... </c> code tags; for shame. Read instructions at the SOPW text-entry box.) And check the O'Reilly site for reports of typos and corrections to whichever edition you're using.

    I suspect there's either a typo or a prior discussion of capturing from STDIN, "Standard In," AKA "the console." If you follow your code at the command line with numbers, hoping that will feed them to split and @numbers the disappointment you specify will occur. But you'll get the expected results if you collect the user's numbers like this:

    C:\>perl -E "print 'your input please: ' ; my $input = <>; say $input; +" your input please: 1 2 32 1 2 32

    And, with the syntax you showed:

    C:\>perl -E "print 'Enter nums: '; use Data::Dumper;@numbers; push @nu +mbers, split while <>; say Dumper @numbers;" Enter nums: 3 5 7 912 11 ^Z $VAR1 = '3'; $VAR2 = '5'; $VAR3 = '7'; $VAR4 = '912'; $VAR5 = '11';
    If I've misconstrued your question or the logic needed to answer it, I offer my apologies to all those electrons which were inconvenienced by the creation of this post.

      6th edition (epub from O'Reilly), page 390, chapter 14 example answers. I looked at the errata on O'Reilly's website and didn't see any mention of it. I'll have to do some looking around.

      And I apologize for my misuse of <c>. I guess I'm used to using it when quoting anything on other forums.

        And in the print version on my desk, question on page 246, answer on page 320.

Re: <> to read command line options?
by Anonymous Monk on Aug 16, 2013 at 14:18 UTC
    Nobody has said it clearly yet. perlop:
    The null filehandle <> is special: it can be used to emulate the
    behavior of sed and awk.  Input from <> comes either from standard
    input, or from each file listed on the command line.  Here's how it 
    works: the first time <> is evaluated, the @ARGV array is checked, and
    if it is empty, $ARGV[0] is set to "-", which when opened gives you
    standard input.  The @ARGV array is then processed as a list of
    filenames.  The loop
    
        while (<>) { 
            ...                     # code for each line
        }
    
    is equivalent to the following Perl-like pseudo code:
    
        unshift(@ARGV, '-') unless @ARGV; 
        while ($ARGV = shift) {
            open(ARGV, $ARGV);
            while (<ARGV>) {
                ...         # code for each line
            }      
        }