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

Why doesn't this Windows .cmd script pick up the -z command line switch:
@rem = '--*-Perl-*-- @echo off perl -x "%~dpnx0" %* goto endofperl @rem '; #!perl use Getopt::Std; my %opts = (); getopt('z', \%opts); print "z\n" if $opts{'z'}; __END__ :endofperl
This does:
@rem = '--*-Perl-*-- @echo off perl -z "%~dpnx0" -z %* goto endofperl @rem '; #!perl use Getopt::Std; my %opts = (); getopt('z', \%opts); print "z\n" if $opts{'z'}; __END__ :endofperl
So, by adding -z to the parameters passed within the script, it prints the 'z' when I run this script with -z. That is to say, it has to be provided by the user and hard-coded in the .cmd script to work.

If I add the following line:

print join(' ',@ARGV)."\n";
then I get this (script is called z.cmd):
C:\>z -z C:\>z -z -z -z z
So, both the -z that is hard-coded, and the -z that is user-supplied, get through to the script, but getopt() only picks it up if both are there.

Updated: Changed example to -z to avoid confusion with the -x parameter to perl itself.

Replies are listed 'Best First'.
Re: Passing switches through pl2bat scripts
by flounder99 (Friar) on Nov 17, 2003 at 15:47 UTC
    Read the man page to Getopt::Std a little closer.
    NAME getopt - Process single-character switches with switch clustering getopts - Process single-character switches with switch clustering SYNOPSIS use Getopt::Std; getopt('oDI'); # -o, -D & -I take arg. Sets $opt_* as a side e +ffect. getopt('oDI', \%opts); # -o, -D & -I take arg. Values in %opts getopts('oif:'); # -o & -i are boolean flags, -f takes an argumen +t # Sets $opt_* as a side effect. getopts('oif:', \%opts); # options as above. Values in %opts
    You want getopts not getopt. Because getopt is expecting a value and getopts is for binary flags. when you do a
    C:\>z -z
    you are setting the value of %opt{z} == "" which is false.
    <code> C:\>z -z -z
    sets the value of %opt{z} == "-z" which is evaluated as true.

    Don't feel bad, I've been there and done that.

    --

    flounder

      you are setting the value of %opt{z} == "" which is false.

      Actually I disagree. The documentation says quite clearly that this behaviour is incorrect:

      The getopt() functions processes single-character switches with switch clustering. Pass one argument which is a string containing all switches that take an argument. For each switch found, sets $opt_x (where x is the switch name) to the value of the argument, or 1 if no argument. Switches which take an argument don't care whether there is a space between the switch and the argument.

      Although I do agree with you that probably he should be using getopts and not getopt, but even better would be to just ditch Getopt::Std and use Getopt::Long instead.


      ---
      demerphq

        First they ignore you, then they laugh at you, then they fight you, then you win.
        -- Gandhi


        But getopt always assumes all the named switches take arguments.
        getopt('oDI'); # -o, -D & -I take arg. Sets $opt_* as a side e +ffect. getopt('oDI', \%opts); # -o, -D & -I take arg. Values in %opts
        Try running this:
        #!perl use Getopt::Std; my %opts = (); getopt('z', \%opts); for (keys %opts) { print '$opts{', $_, '} = "', $opts{$_}, "\"\n"; }
        you get some interesting results
        C:\myperl\temp>perl temp.pl -z $opts{z} = "" C:\myperl\temp>perl temp.pl -y $opts{y} = "1" C:\myperl\temp>perl temp.pl -y -z $opts{y} = "1" $opts{z} = "" C:\myperl\temp>perl temp.pl -z -y $opts{z} = "-y" C:\myperl\temp>temp -z -z $opts{z} = "-z"

        --

        flounder

Re: Passing switches through pl2bat scripts
by ptkdb (Monk) on Nov 17, 2003 at 14:12 UTC
    Just spitballing a posibility

    perl -x
    This enables the feature that allows perl to read stdin until it sees the '#! perl' and then start reading the input until __END__. As an argument to the perl interpreter itself, it's never seen as part of the @ARGV array of the script, so 'getopt' will never see it

    Your next -x however, is being passed to the script as an argument to the script itself, so 'getopt' can see it.

      The first -x is a red herring, and perhaps I should have chosen a better example and avoided using -x in my script. However, as the results of join(' ',@ARGV)."\n"; demonstrate, the perl script ends up with a -x that is not processed by getopt(). Here's a better example:
      @rem = '--*-Perl-*-- @echo off perl -x -S "%~dpnx0" -z %* goto endofperl @rem '; #!perl use Getopt::Std; print join(' ',@ARGV)."\n"; my %opts = (); getopt('z', \%opts); print "z found\n" if $opts{'z'}; __END__ Results: C:\>x -z C:\>x -z -z -z z found :endofperl
        The issue is basically the same. At some point 'perl' stops accepting arguments/options to the interpreter itself and starts handing them to the script as @ARGV.

        In a typical invocation

        perl @perlOpts filename.pl @scriptOpts

        everything after filename.pl shows up in @ARGV.

        The question becomes, where is that point in your invocation of 'perl' in this particular context?

Re: Passing switches through pl2bat scripts
by demerphq (Chancellor) on Nov 17, 2003 at 16:12 UTC

    Why doesn't this Windows .cmd script pick up the -z command line switch:

    It does. Try the following code with the argument of -z passed through from the outside:

    @rem = '--*-Perl-*-- @echo off perl -x "%~dpnx0" %* goto endofperl @rem '; #!perl use strict; use warnings; print "\$ARGV[$_]= $ARGV[$_]\n" for 0..$#ARGV; use Getopt::Std; my %opts = (); getopt( 'z', \%opts); print "exists z\n" if exists $opts{'z'}; print "defined z='$opts{'z'}'\n" if defined $opts{'z'}; print "\$opts{z} is true" if $opts{'z'}; __END__ :endofperl

    You will see that the -z is correctly propagated from the batch wrapper into perl. You will also see that $opts{'z'} exists, but contains an undefined value. It looks like from the documentation this behaviour is incorrect, and that it should in fact set $opts{'z'} to be true in this situation. It also looks like this behaviour has been fixed in a later version of Getopt::Std. The 5.8.1 version doesnt have this problem, the 5.6.1 version does. UPDATE: No im wrong here, changes were made to this module between 5.6.1 and 5.8.1 but they dont cover this issue.

    Incidentally the reason this worked in your second variant is because the second -z is being treated as the value for the first -z option. IOW, $opts{'z'} eq '-z' in the second case, and !defined $opts{'z'} and exists($opts{'z'}) is true in the first.

    I feel entitled to be a little critical here. It didnt take long to determine this was a fault in Getopt::Std, nor to identify and fix the bug. Next time I suggest you be a bit more thorough in your testing and debugging. Also you may find that Getopt::Long while a bit intimidating to look at the first time actually is a more flexible and powerful tool for this job. It certainly wouldnt have left you as confused about this as Getop::Std has done.

    HTH


    ---
    demerphq

      First they ignore you, then they laugh at you, then they fight you, then you win.
      -- Gandhi


      I feel entitled to be a little critical here. It didnt take long to determine this was a fault in Getopt::Std, nor to identify and fix the bug.
      Bug? The bug is in Getopt::Std, and I think it is worthy of disucssion. How do I take this further? I ought to inform the author or maintainer of the package. What's the ettiquette here? Should I just email the author, or should I subscribe to some kind of mailing list?
      The docs aren't wrong, just very confusing. See my comment above.

      --

      flounder

        I beg to differ. The docs are wrong in one way or the other. Consider the 5.6.1 release of the module. It has this to say:

        The getopt() functions processes single-character switches with switch clustering. Pass one argument which is a string containing all switches that take an argument. For each switch found, sets $opt_x (where x is the switch name) to the value of the argument, or 1 if no argument. Switches which take an argument don't care whether there is a space between the switch and the argument.

        the 5.8.x version has this to say

        The getopt() function processes single-character switches with switch clustering. Pass one argument which is a string containing all switches that take an argument. For each switch found, sets $opt_x (where x is the switch name) to the value of the argument if an argument is expected, or 1 otherwise. Switches which take an argument don't care whether there is a space between the switch and the argument.

        So in 5.6.1 the documentation does not agree with the behaviour of the module at all. So somethings wrong. In 5.8.1 the documentation discusses a case that doesn't exist. There are no circumstances where there isnt a value expected for an argument being handled by getop(). So something is wrong with the documentation.

        IMO the most reasonable behaviour is that which PhillHibbs was expecting which is the one that the docs in the 5.6.1 release specify, namely that the option should be set to 1.

        However the more I read of Getopt::Std the less suitable I think it is for on trivial usage. Im glad I never wasted any of my real time using it, as I went straight for Getopt::Long.


        ---
        demerphq

          First they ignore you, then they laugh at you, then they fight you, then you win.
          -- Gandhi