http://qs1969.pair.com?node_id=372438

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

I am writing a Perl program to do some parsing. It will be used from the command line as:

myprogram <your file>

I am using the diamond operator to get the data out of the file. I want to write a section that checks to see if the user specified anything at the command line and if they did not, I want to give them some help as far as how to use the program.
I tried checking <> several ways with no success:
if (<> == "") { some helpful code } if (<> eq "") { some helpful code } if (<> == 0) { some helpful code }
So clearly I am ignorant of how to check the diamond operator for the existance of data. Please help me :-)

edit (broquaint): added <code> tags

Replies are listed 'Best First'.
•Re: Testing <> for undefined
by merlyn (Sage) on Jul 07, 2004 at 16:30 UTC
    If you're asking how to see if there are command line arguments, the arguments are copied into @ARGV. Check the length of that array. If it's empty, then you have no args:
    if (@ARGV) { # you have command line args } else { # ya don't! }

    -- Randal L. Schwartz, Perl hacker
    Be sure to read my standard disclaimer if this is a reply.

Re: Testing <> for undefined
by dws (Chancellor) on Jul 07, 2004 at 16:30 UTC

    Your best bet is to put input into a variable, and test from here. That avoids icky little "but I didn't mean to read twice!" bugs. Also, direct comparisons are going to be problematic unless you remove the trailing newline that comes with the input. E.g.,

    my $input = <>; chomp($input); # remove trailing \n if ( $input eq "" ) { ... }

    But none of that helps when you need to be looking at @ARGV instead.

Re: Testing <> for undefined
by gellyfish (Monsignor) on Jul 07, 2004 at 16:36 UTC

    You don't want to test the diamond operator, you want to test whether any command line arguments were supplied:

    if ( @ARGV == 0 ) { print "Something useful\n"; }

    /J\

Re: Testing <> for undefined
by Joost (Canon) on Jul 07, 2004 at 16:31 UTC
Re: Testing <> for undefined
by Roy Johnson (Monsignor) on Jul 07, 2004 at 16:32 UTC
    Command line arguments are in the array named @ARGV. The diamond operator gets lines from whatever the current input file is.

    If you get a line from a file and test it as in your examples, you will no longer have that line to play with. You want to fetch it into a variable and then do your tests, etc.


    We're not really tightening our belts, it just feels that way because we're getting fatter.
Re: Testing <> for undefined
by TrekNoid (Pilgrim) on Jul 07, 2004 at 16:46 UTC
    Here's something that I use in most of my scripts:

    Assuming the scriptname is called file_parse.pl, and you're trying to pass it single file only:

    if ($#ARGV != 0) { print "Usage: file_parse.pl [filename] \n"; exit(1); }
    Trek
      This code scares me. If you want to see if there is one argument, use @ARGV in a scalar context, comparing it to one. I could easily imagine people reading this code and not remembering that $#ARGV is one less than the number of arguments, and being very confused.

      My rule is, use @ARGV when you want to talk about the number of elements, and reserve $#ARGV for getting the ending index for ranges.

      -- Randal L. Schwartz, Perl hacker
      Be sure to read my standard disclaimer if this is a reply.

        My rule is, use @ARGV when you want to talk about the number of elements, and reserve $#ARGV for getting the ending index for ranges.

        Thanks, merlyn... It's one of those things I learned to do one way when I was first learning Perl, and have never gone back and revisted it...

        There's just so few things I can help people with in Perl, that I get excited when I see one I *can* answer :) Guess I'm destined to remain an acolyte for the foreseeable future :)

        I'll make a note for future development

        Trek

Re: Testing <> for undefined
by danielcid (Scribe) on Jul 08, 2004 at 13:46 UTC
    You could also do something like that:
    # Test if the is an argument if(!@ARGV) { die "My help message\n"; } # Test if the argument is a file if(! -e $ARGV[0]) { print "File $ARGV[0] does not exist\n"; }
    []'s -DBC

    Edited by Chady -- code tags, escaped a [

Re: Testing <> for undefined
by danielcid (Scribe) on Jul 08, 2004 at 15:59 UTC
    *fixing my previous post:

    You could also do something like that:
    # Test if the is an argument if(!@ARGV) { die "My help message\n"; } # Test if the argument is a file if(! -e $ARGV[0]) { print "File $ARGV0 does not exist\n"; }
    's -DBC
      I have done this in the past, and it seems to work fine:
      my $file = $ARGV[0] ? $ARGV[0] : die "USAGE:  prog.pl file_pathname\n";
      open(FILE,"$file") or die "Unable to open file $file\n";
        I have to say that I hate your use of the trinary operator there. You're expressing that you want to assign die's result to $file. Instead:
        my $file = $ARGV[0] or die "Usage: prog.pl file_pathname\n";
        (as a bonus, you don't have the redundant mention of $ARGV[0]).
        Better, if the filename might be zero:
        @ARGV or die "Usage: prog.pl file_pathname\n"; my $file = $ARGV[0];

        We're not really tightening our belts, it just feels that way because we're getting fatter.
Re: Testing <> for undefined
by graff (Chancellor) on Jul 08, 2004 at 23:38 UTC
    Here's something that is relevant to your question, but has not been explained yet in the other replies that I read. Part of the magic of  while (<>) is that it will handle all of the following types of command line usage:
    # name one file: my_script.pl input.file # name multiple files: my_script.pl first.file second.file ... # use redirection from a file to STDIN: my_script.pl < some.file # use a pipeline to STDIN: other_process | my_script.pl # e.g.: cat *.file | my_script.pl
    That is, if one or more files are named as args to the script, a  while (<>) loop will step through and read each file in turn. On the other hand, if data is being fed to the script on its STDIN (via redirection or pipeline),  while (<>) will read that.

    (The default behavior does not let you do both: if files are named as args, the plain, empty diamond operator reads the files and does not read STDIN.)

    Anyway, the reason why you have to check @ARGV first, and not test the diamond operator, is that when @ARGV is empty, the script will wait until there is either input data (i.e. one whole line) or an EOF condition on STDIN, and you won't be able to check the result of the diamond operator until one of those things happens.

Re: Testing <> for undefined
by jdavidboyd (Friar) on Jul 10, 2004 at 00:11 UTC
    Okay, here's something I don't understand.

    Suppose I have the following code, I'll call it prog.pl:

    #!/usr/bin/perl my $lines; my @lines; print ("\@ARGV: @ARGV, \$\#ARGV: $#ARGV\n"); if (!@ARGV){ print("No command line parameters present"); } chomp(@lines = <>); foreach $lines (@lines){ print("$lines\n"); }


    1. If I run the program as perl prog.pl test.txt it prints out

    @ARGV: test.txt, $#ARGV: 0

    2. If I run the program as perl prog.pl it prints out

    @ARGV: , $#ARGV: -1

    (This next one is my problem.)

    3. If I run the program as perl prog.pl < test.txt it prints out

    @ARGV: , $#ARGV: -1

    which looks as if there were no command line arguments.

    Now, I realize that in some aspects, this might be considered correct,
    as there are no command line arguments, just input to the <> operator.
    Will GetOpt get around this peculiarity?
    Or am I going to be stuck always showing my little help text no matter what?

    Am I missing something dreadfully obvious?
      When you invoke the perl interpreter (i.e. "perl") on the command line, and give it the name of a script file to load and execute, then any command-line args that follow the name of the script file are passed directly to the script as the contents of @ARGV. If nothing follows the name of the script file, then the script gets an empty @ARGV.

      If the script file itself is treated by the shell/OS as an executable file, so that the name of the script file can be the first thing on the command line, the behavior is the same: any args following the name of the script file are provided to the script in @ARGV.

      Of course, if the things that follow the script name happen to have special meaning to the shell, like a redirection operator (< or >), logic/process-control operator (& or vertical bar), or other things (depending on your shell), then the shell will do what it is supposed to do with such args before working out the strings to be passed to your script as @ARGV.

      So in a command like  myscript.pl < my.input (or the version that uses "perl" at the beginning), the angle bracket and the arg after it are treated by the shell as things to be handled by the shell, and these are not passed on to the perl script's @ARGV. This is why, if your script is set up to accept args that contain such characters, you need to quote or escape them on the command line, so that the shell will pass them to the script verbatim rather than acting on them itself.

        Ah, thank you very much.

        I hypothetically knew this, but I had never really internalized it.

        Thanks for the explanation!