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

Hello Monks,

I have been introduced recently to the Getopt::Long and I was trying to create my first example by storing ARGV inputs into an array and checking for the extension '*.xls' and '*.csv' through a regex expression.

My code was copied and modified from Examples for using Getopt::Long.

Although that I am applying the same technique to the last argument of ARGV and works just fine I can not find a way to applied it also on the nth - 1 elements. The problem appears because of the -f extension to my '*.xls' files. I need to find a way to separate the -f extension from all the arguments except of the last one. Because the last one has a different extension -p. To get the last element of the ARG I am using pop function.

I observed that ARGV also take in consideration the -f and -p options that I am using as input from terminal, while I was processing the values.

If any one feels more familiar and can provide any thoughts to my problems I would be great.

Update

My target is to create a dynamic argument input and check one by one the arguments for '*.xls' and '*.csv' extensions. So far, my code works fine for the last argument input, but the problem appears when I am trying to check the rest of the inputs.

Sample of input: perl test.pl -f test.xls -f test-2.xls -f test-3.xls -p sample.csv. At this point I can detect the error if the user did not provide a '*.csv'. My problem is that I can not check one by one the other argument inputs and store them in the array @xls.

I modified the code based on my last update, but again I got stuck. Based on my last modification, the code compiles without any errors but it can not detect the '*.xls' elements one by one.

Expected output would be (taken in consideration the input):

$VAR1 = [ 'test.xls', 'test-2.xls', 'test-3.xls' ];

I am trying to separate the -f option of every argument input so I can extract only the files test.xls test-2.xls test-3.xls and store them into the @xls array as an output.

I am using the syntax taken from Getopt::Long (perldoc), because on the documentation says:

foo=s{1,} indicates one or more values; foo:s{,} indicates zero or more option values.

In my case I need to have at least one argument input, and the final number unknown.

Sample of working code:

#!/usr/bin/perl use strict; use warnings; use Getopt::Long(); sub usage { my $message = shift(@_); if (defined $message && length $message) { $message .= "\n" unless $message =~ /\n$/; } my $command = $0; $command =~ s#^.*/##; print STDERR ( $message, "usage: $command -f file(s).xls -p file.csv\n" ); die("\n") } my @xls; my $csv; print "This is the number of \@ARGV arguments before the process ".@AR +GV."\n"; Getopt::Long::GetOptions( 'f=s{1,}' => sub { foreach $_ (@_) { print $_ . "\n"; /\.xls$/ or die ("Invalid format for option expected -f '*.xls'\n"); push(@xls,$_); } }, 'p=s' => sub { $_ = pop @_; /\.csv$/ or die ("Invalid format for option expected -p '*.csv'\n"); $csv = $_; } ) or usage("Invalid commmand line options."); usage("The '*.xls' file(s) needs to be specified.") if scalar (@xls == 0); usage("The '*.csv' file needs to be specified.") unless defined $csv; print "Input given form \@xls: ".@xls."\n"; print "Input given form \$csv: ".$csv."\n"; =test 'f=s{1,}' => sub { foreach $_ (@_) { print $_ . "\n"; /\.xls$/ or die ("Invalid format for option expected -p '*.xls'\n"); =cut
Output Update

If I execute the code with input arguments (-f test.xls -p test.csv) I get:

This is the number of @ARGV arguments before the process 4 f Invalid format for option expected -f '*.xls' Invalid commmand line options. usage: long.pl -f file(s).xls -p file.csv

Which it makes sense based on my observations. Because I can see that the inputs are collected normally but I am reading the -f as an input.I have to figure out how to read the argument inputs all apart from the last one, and then remove the -f option in order to keep only the file arguments.

I have commented the last part of my code where I am checking for the '*.xls' extension one by one the inputs. This is also a problem because of the -f option but I will deal with it after.

Thank you in advance for your time and effort reading and replying to my question.

Final Update and answer to Question

Well I understood the problem was coming from the arguments -f. The Getopt::Long module id so nicely written that can understand based on the input "extension" -f or -p of the user (arguments) where to allocate them. So my problem was that although the input is split into two different inputs I was not able to split the arguments with -f notation. Then I thought more about it and I understood that they are still arguments. Then it was really simple, I just put 2 conditions to separate them and solved!

Solution code:

Getopt::Long::GetOptions( 'f=s{1,}' => sub { foreach $_ (@_) { if ($_ =~ /\.xls$/) { print "This is if \$_: ".$_."\n"; push( @xls , $_ ); } elsif ($_ =~ /^f/) { next; } else { die ("Invalid format for option expected -f '*.xls'\n"); } } # End of foreach }, 'p=s' => sub { $_ = pop @_; /\.csv$/ or die ("Invalid format for option expected -p '*.csv'\n"); $csv = $_; } ) or usage("Invalid commmand line options.");

Again thanks everyone time and effort to assist me.

1 Seeking for Perl wisdom...on the process...not there...yet!

Replies are listed 'Best First'.
Re: Getopt::Long::GetOptions checking input array for file extensions
by toolic (Bishop) on Aug 08, 2014 at 02:56 UTC
    The reason @xls is empty is because you did not assign it any values. Typically, this is done by passing a variable reference to GetOptions for it to assign values to the variable (but using a sub might also accomplish this). It's not clear to me yet what you are trying to do. Please update your post with expected values for your variables.

    UPDATE:

    use strict; use warnings; use Getopt::Long(); sub usage { my $message = shift(@_); if ( defined $message && length $message ) { $message .= "\n" unless $message =~ /\n$/; } my $command = $0; $command =~ s#^.*/##; print STDERR ( $message, "usage: $command -f file(s).xls -p file.c +sv\n" ); die("\n"); } my @xls; my $csv; print "This is the number of \@ARGV arguments before the process " . @ +ARGV . "\n"; Getopt::Long::GetOptions( 'f=s' => \@xls, 'p=s' => \$csv, ) or usage("Invalid commmand line options."); usage("The '*.xls' file(s) needs to be specified.") if scalar( @xls == 0 ); usage("The '*.csv' file needs to be specified.") unless defined $csv; print "Input given form \@xls: " . @xls . "\n"; print "Input given form \$csv: " . $csv . "\n"; use Data::Dumper; print Dumper(\@xls); __END__ Here is my output: This is the number of @ARGV arguments before the process 6 Input given form @xls: 2 Input given form $csv: test.csv $VAR1 = [ 'sample.xls', 'test.xls' ];

      Hello toolic,

      Thank you for your time and effort to assist me with my problem. I have updated the code and also the explanation to my problem. Apologies if it was not so clear from the beginning.

      Seeking for Perl wisdom...on the process...not there...yet!
Re: Getopt::Long::GetOptions checking input array for file extensions
by Athanasius (Archbishop) on Aug 08, 2014 at 03:03 UTC

    Hello thanos1983,

    I see three problems in your code (there may be others):

    1. You are not saving anything to @xls:

    2. Getopt::Long::GetOptions ( 'f=s{1,}' => sub { print "This is the number of \@ARG arguments inside the proces +s " . @ARGV . "\n"; print "This is the number of \@xls arguments inside the proces +s " . @xls . "\n"; push @xls, pop @_; # <-- Add this }, ...
    3. In this condition: if scalar (@xls == 0); the parentheses apply scalar to the result of the == comparison. You need either if (scalar @xls) == 0; or just if scalar @xls == 0; — or drop the scalar altogether: if @xls == 0.

    4. If you want to print out the contents of @xls, change

      print "Input given form \@xls: ".@xls."\n";

      to

      print "Input given form \@xls: ", join(', ', @xls), "\n";
    5. (because concatenation puts @xls into scalar context).

    Hope that helps,

    Athanasius <°(((><contra mundum Iustus alius egestas vitae, eros Piratica,

      Hello Athanasius,

      Thank you for your time and effort to assist me with my problem. I have updated the documentation and also the output of the code. Nice tips about the scalar @xls I was not aware the problem.

      I am trying to process all arguments given with the extension -f test.xls -f test-2.xls -f test-3.xls etc.

      I want to separate the -f option from the rest of the argument, in order to keep only the arguments with '*.xls' extension. But the tricky part is that I also need to take care of the last argument -p sample.csv. I was assuming by using the f=s{1,} in my script it will take only in consideration the -f arguments and ignore the -p arguments. Which it will simplify my target at least. But this is another challenge to over come.

      Again thank you for your time and effort.

      Seeking for Perl wisdom...on the process...not there...yet!
Re: Getopt::Long::GetOptions checking input array for file extensions
by Athanasius (Archbishop) on Aug 08, 2014 at 14:22 UTC

    Hello again thanos1983,

    Glad to see from the final update that you’ve got the solution you were looking for. One point: in the regex

    /.xls$/

    Perl treats the dot as a metacharacter which means “Match any character (except newline)”.1 So the regex matches a file name ending in the letters “xls” preceded by any character. To match a full stop only, you need to backslash it:

    /\.xls$/

    In most cases, this won’t make any difference to your results, but if you happened to have a file named “abcxls” this would currently be matched incorrectly.

    1See “Metacharacters” in perlre#Regular-Expressions.

    Hope that helps,

    Athanasius <°(((><contra mundum Iustus alius egestas vitae, eros Piratica,

      Hello Athanasius,

      Nice!!! I am not really familiar with the "regex" so any advice is always welcome. I have been observing this backslash in several examples with "regex" and I was not able to understand the reason of existence.

      Thanks for the tip, as a friend of mine says "The devil lays on the details". ;)

      Seeking for Perl wisdom...on the process...not there...yet!