perlperldoc

perl -s switch documentation confusion


Examples on stackoverflow involving -s switch (e.g. https://stackoverflow.com/a/29613573/14557599) give position of -- like:

perl -s -e 'if ($xyz) { print "$xyz\n" }' -- -xyz

perldoc perlrun:

-s   enables rudimentary switch parsing for switches on the command line
     after the program name but before any filename arguments (or before
     an argument of --). Any switch found there is removed from @ARGV
     and sets the corresponding variable in the Perl program. The
     following program prints "1" if the program is invoked with a -xyz
     switch, and "abc" if it is invoked with -xyz=abc.

         #!/usr/bin/perl -s
         if ($xyz) { print "$xyz\n" }

I'm confused with "before an argument of --", because example above works, -xyz is after --. If written as in docs:

perl -xyz -- -s -e 'if ($xyz) { print "$xyz\n" }' 
Can't open perl script "-s": No such file or directory

Per my understanding how it works I want to write in doc:

after the program name (or after an argument of --) but before any filename arguments

Is my change correct? Maybe sometime before syntax of perlrun was indeed as described in docs but was changed later but docs were updated to reflect that?

Edit 2:

After reading all the answers:

after the program name (or after first occurrence of an argument of -- if the program is specified via -e or -E switches) but before any filename arguments (or before --, first occurrence in case of program name and second in case of the program specified via -e or -E switches)

TL;DR

Edit:

In response to am answer by zdin (note: I have not found definition of option, switch; seems those are synonyms and both are used as both bash command line arguments and in perl program on shebang line arguments).

echo a-a-a > 1.txt

q.pl

#!/usr/bin/perl -sp
s/a/b/;
if ($xyz) { print "$xyz\n" }

w.pl

#!/usr/bin/perl -s
s/a/b/;
if ($xyz) { print "$xyz\n" }

I can call *.pl passing 1.txt as argument w/out --:

./q.pl -xyz=a 1.txt 
a
b-a-a

perl -p ./q.pl -xyz=a 1.txt 
a
b-a-a

One-liner:

perl -spe 's/a/b/;if ($xyz) { print "$xyz\n" }' -- -xyz=a 1.txt
a
b-a-a

That shows my reformulation is valid for both running via program name and one-liners.

P.S.

perl --version

This is perl 5, version 34, subversion 0 (v5.34.0) built for x86_64-linux-gnu-thread-multi

Solution

  • The quoted statement in perldoc is correct, if incomplete as it doesn't mention a different behavior for a one-liner. The confusion stems from peculiarities of option parsing with -s in presence.

    That particular statement refers to running a program-in-a-file, as shown. With

    #!/usr/bin/perl -s
    
    use warnings;
    use strict;
    use feature 'say';
    
    our $tt;  # -s populates package variables
    
    say $tt // "no -tt...";
    

    we can do

    perl test_s_switch.pl -tt=X -- arguments
    

    to mark the end of options ("switches"), after which arguments come (for @ARGV). This is needed if the first of the following arguments happens to look like an option (starts with -); or we can leave out -- otherwise (if the first argument doesn't start with -), or if no arguments are being passed

    perl test_s_switch.pl -tt=X
    

    These print X. (Or perl -s test_s_switch.pl ... w/o the -s on the shebang line.)

    But for a command-line program ("one-liner") it goes the way you observe

    perl -s -wE'say $tt // "no -tt"' -- -tt=X
    

    and we get X printed. Here the -- is necessary to indicate end of options so that after it go arguments. This distinction is stated at the beginning of Command Switches in perlrun

    A -- signals the end of options and disables further option processing. Any arguments after the -- are treated as filenames and arguments.

    The reason for this difference is that with the program given in a file the options ("switches") must come before the program-filename, what leaves the place for -tt=X (argument for -s) to come right after the program-filename, before -- or arguments. But with a command-line program ("one-liner") the program itself is a part of a switch (-e'...') so other options may follow, so to pass an argument for -s (-tt=X) we must have -- first, to indicate the end of options. Tricky, eh.

    With a one-liner, trying without -- makes -tt=X into switches (t, t, and =X)

    perl -s -wE'say $tt // "no -tt"' -tt=X   # um, no...
    

    and results in

    Unrecognized switch: -=X  (-h will show valid options).
    

    since -t is a legitimate switch and repeating it (-tt) is accepted.


    At this point I have to note a remark at the end of the -s switch description in perlrun

    ...
    For these reasons, use of -s is discouraged. See Getopt::Long for much more flexible switch parsing.