My case at hand
An overgrown one-liner with 10 -s
switch-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
-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?