0

My case at hand

An overgrown one-liner with 10 -sswitch-powered arguments,
that I desire to put into a file script whith /usr/bin/env-powered shebang.
10 arguments make me wish for a straightforward solution.

Problem

In a one-liner you can use -s switch for handier arguments:

> perl -sE 'map say, $arg1, $arg2, $argbool' -- -arg1=one -arg2=two -argbool
one
two
1

You also can use -s switch for a file-stored code if your shebang directs to perl itself:

> cat perl_s_args
#! /usr/bin/perl -s
use feature 'say';
map say,
  $arg1,
  $arg2,
  $argbool
> ./perl_s_args -arg1=one -arg2=two -argbool
one
two
1

However, with use strict;, for each variable it complains:
Variable "$..." is not imported at /tmp/perl_s_args line ....
Global symbol "$..." requires explicit package name (did you forget to declare "my $..."?) at /tmp/perl_s_args line ....

However still, if you try to make your shebang more portable via env:

#! /usr/bin/env perl -s

then on my OpenBSD machine:

> ./perl_s_args -arg1=one -arg2=two -argbool
env: perl -s: No such file or directory

Workaround

For an env shebang, consider this workaround:

> cat workaround_1
#! /usr/bin/env perl

use strict;
use warnings;
use Config;

# cross-OS - append extension like `.exe` if needed
my $perl_exe_path = do {
    if        ($^O eq 'VMS')                            { $Config{perlpath}                 }
    else { if ($Config{perlpath} =~ m/$Config{_exe}$/i) { $Config{perlpath}                 }
           else                                         { $Config{perlpath} . $Config{_exe} } }};

# the code we want to run
my $perl_code = <<~ 'END_OF_CODE';
    map say,
        $arg1,
        $arg2,
        $argbool
    END_OF_CODE

my $_ARGV = join ' ', @ARGV;

# run as a one-liner
my @output = qx{ $perl_exe_path -sE '$perl_code' -- $_ARGV };
print @output;
> ./workaround_1 -arg1=one -arg2=two -argbool
one
two
1

What it does is it launches second perl in a "one-liner mode" so that -s switch is available again and passes the initial user's arguments to it. Thus, the wrapper allows for pretty-formatted multiline code, while the second "one-liner perl" handles arguments.
The my $perl_exe_path = do {... is IMHO (YMMV) clearer version of stock

# https://perldoc.perl.org/perlvar#$EXECUTABLE_NAME
use Config;
my $secure_perl_path = $Config{perlpath};
if ($^O ne 'VMS') {
    $secure_perl_path .= $Config{_exe}
    unless $secure_perl_path =~ m/$Config{_exe}$/i;
}

As you can see, this workaround is not the most elegant solution.
There are shell-wrapper workaround answers How to use /usr/bin/env perl functionality along with perl arguments? yet they get whatever perl is first in the user's path. The code above OTOH invokes the same perl it is invoked with. Makes sense if you run /not/in/PATH/perl scriptfile -args or if PATH gets changed.

Questions

Is there a better approach? A direct and easy and uniform way to use -s switch argument handling mechanism, without reimplementing it via Getopt or smth. and without workarounds, for all 3 cases:

  • one-liner
  • #! /usr/bin/perl shebang file
  • #! /usr/bin/env perl shebang file

Also, -- separator is mandatory for one-liner, but must be omitted for file-based script invocation. Is there a way to make it work uniformly?

Also, why perl has use ... equivalents for some switches, e.g. use warnings vs. -w, even if

https://stackoverflow.com/a/593272/14812514

-w will apply to all packages that you use, use warnings will only apply lexically. You typically do not want to use or rely upon -w

but not for all switches wherever reasonable? E.g. the -s switch (apparently) has no use switches 's' equivalent.
In other words, where is the TIMTOWTDI? With TIMTOWTDI, we should be able to access this functionality via either switches or use ... — whichever.
Why such limitations?
Are there plans to fix it?

uxer
  • 521
  • 3
  • 10
  • 3
    If your script has outgrown being a one liner, you should be looking to avoid having the command line decide what variables exists, not trying to recreate that horrible model which is only acceptable for trivial scripts – ikegami Oct 24 '22 at 22:31
  • 6
    The replacement for `-s` is the [`Getopt::Long` module](https://perldoc.perl.org/Getopt::Long). – Shawn Oct 25 '22 at 01:35

0 Answers0