I am migrating an old toolchain to a new system and now I get plenty of notifications given is experimental or when is experimental.
$ perl -e 'use v5.10; given (12) { when (12) { print "Hello World" }}'
given is experimental at -e line 1.
when is experimental at -e line 1.
Hello World
I would like my new system to be fully compatible with the old one. By this I mean the exact same output.
Is there a way to mute these notifications without touching the oneliners nor the scripts?
First of all, note that smartmatching will be removed or changed in a backwards incompatible manner. This may affect your given statements.
To use given+when without warnings, one need the following:
# 5.10+
use feature qw( switch );
no if $] >= 5.018, warnings => qw( experimental::smartmatch );
or
# 5.18+
use feature qw( switch );
no warnings qw( experimental::smartmatch );
experimental provides a shortcut for those two statements.
use experimental qw( switch );
Finally, you ask how to add this to your programs without changing them (and presumably without changing Perl). That leaves monkeypatching.
I wouldn't recommend it. It's far easier to write a couple of one-liners to automatically fix up your programs that rewritting Perl's behaviour on the fly.
But if you want to go in that direction, the simplest solution is probably to write a $SIG{__WARN__} handler that filters out the undesired warnings.
$SIG{__WARN__} = sub {
warn($_[0]) if $_[0] !~ /^(?:given|when) is experimental at /;
};
(Of course, that won't work if your program makes use of $SIG{__WARN__} already.)
To get it loaded without changing your programs or one-liners, all you have to do is place the patch in a module, and tell Perl to load the module as follows:
export PERL5OPT=-MMonkey::SilenceSwitchWarning
$ cat Monkey/SilenceSwitchWarning.pm
package Monkey::SilenceSwitchWarning;
use strict;
use warnings;
$SIG{__WARN__} = sub {
warn($_[0]) if $_[0] !~ /^(?:given|when) is experimental at /;
};
1;
$ perl -e 'use v5.10; given (12) { when (12) { print "Hello World\n" }}'
given is experimental at -e line 1.
when is experimental at -e line 1.
Hello World
$ export PERL5OPT=-MMonkey::SilenceSwitchWarning
$ perl -e 'use v5.10; given (12) { when (12) { print "Hello World\n" }}'
Hello World
no warnings 'experimental';
...works in version 5.22 at least.
Related
TLDR: See question title.
In the original version of a question, there was use strict (); at the top of the program. No variables were declared with my. The program worked. I pointed that out that the code wouldn't work because of the missing mys, but it turns out I was mistaken.
$ perl e 'use strict (); $foo = 1'
This program works. It doesn't crash. But obviously this crashes:
$ perl -e 'use strict; $foo = 1'
Global symbol "$foo" requires explicit package name (did you forget to declare "my $foo"?) at -e line 1.
Execution of -e aborted due to compilation errors.
My first idea was to deparse it to see if something else is going on.
$ perl -MO=Deparse -e 'use strict (); $foo = 1'
use strict ();
$foo = 1;
-e syntax OK
But it's not. Then it got me thinking, you can do use strict 'vars', which only turns on the vars thing1. It obviously does that by calling import.
The use strict is the same as BEGIN { require strict; strict->import; }, so the same rule that goes for modules should apply for pragmata too. If I do use Foo (), nothing is imported. Hence, use strict (); should be the same as require strict;, just at run time, because nothing is imported.
$ perl -e 'require strict; $foo = 1'
This doesn't crash. But then at runtime you cannot turn on stuff that is supposed to be set at compile time.
So what does this actually do? When Perl reaches my actual code, it probably already encountered the strict pragma somewhere else, so it wouldn't load it again. And it doesn't import anything.
$ perl -e 'print %INC'
Oops. This prints nothing. %INC is empty. But if we use another module, there is something in it.
$ perl -MData::Dumper -e 'print Dumper \%INC'
$VAR1 = {
'warnings.pm' => '/usr/share/perl/5.22/warnings.pm',
'overload.pm' => '/usr/share/perl/5.22/overload.pm',
'Carp.pm' => '/usr/share/perl/5.22/Carp.pm',
'strict.pm' => '/usr/share/perl/5.22/strict.pm',
'overloading.pm' => '/usr/share/perl/5.22/overloading.pm',
'constant.pm' => '/usr/share/perl/5.22/constant.pm',
'bytes.pm' => '/usr/share/perl/5.22/bytes.pm',
'Data/Dumper.pm' => '/usr/lib/x86_64-linux-gnu/perl/5.22/Data/Dumper.pm',
'XSLoader.pm' => '/usr/share/perl/5.22/XSLoader.pm',
'Exporter.pm' => '/usr/share/perl/5.22/Exporter.pm',
'warnings/register.pm' => '/usr/share/perl/5.22/warnings/register.pm'
};
If we load Data::Dumper, strict was loaded at some point. But not in the pure -e example.
$ perl -e 'use strict (); print %INC'
strict.pm/usr/share/perl/5.22/strict.pm
Ok. This loads strict.pm.
$ perl -e 'require strict; print %INC'
strict.pm/usr/share/perl/5.22/strict.pm
So does that. But still, none of the strict things are enabled.
So the question really is, is use strict () equivalent to just not having a use strict statement at all, or is there something else happening?
1) perldoc strict refers to the three different arguments strict can take as things
use strict ();
(essentially) doesn't do anything: see the docs:
If you do not want to call the package's import method (for instance, to stop your namespace from being altered), explicitly supply the empty list:
use Module ();
That is exactly equivalent to
BEGIN { require Module }
Of course, strict only does something if you call its import method (which you can easily verify by reading its source code; it's only about 150 lines long). So bypassing the import method bypasses the entire effect of use strict.
I want my script to print a help message when it is run with the --help command line option. Based on the Getopt::Std documentation, this sub should do the trick:
#!/usr/bin/env perl
use strict;
use warnings;
use 5.014;
use Getopt::Std;
sub HELP_MESSAGE {
say "HELP MESSAGE";
}
But it prints nothing. I also tried adding this, out of curiosity:
for (#ARGV) {
HELP_MESSAGE() if /--help/;
}
It actually works, but seems rather sloppy. I know using the -h flag would be quite simple, but I would like to have both.
The documentation of Getopt::Std says
If - is not a recognized switch letter, getopts() supports arguments --help and --version. If main::HELP_MESSAGE() and/or main::VERSION_MESSAGE() are defined, they are called; ...
So try this:
#!/usr/bin/env perl
use strict;
use warnings;
use 5.014;
use Getopt::Std;
$Getopt::Std::STANDARD_HELP_VERSION = 1;
our $VERSION = 0.1;
getopts(''); # <<< You forgot this line, and `getopt()` DOES NOT work
sub HELP_MESSAGE {
say "HELP MESSAGE";
}
Test run:
$ ./t00.pl --help
./t00.pl version 0.1 calling Getopt::Std::getopts (version 1.07),
running under Perl version 5.16.3.
HELP MESSAGE
I have a failing test on 5.8.8, I don't understand why, esp when it works in more recent versions (perhaps it was just a bug) (here's a link to the full code)
use strict;
use warnings;
use Test::More;
my $fname = 'Fo';
my $content = do { local $/ ; <DATA> };
like $content, qr/^$fname $/xms, q[includes first name];
done_testing;
__DATA__
use strict;
use warnings;
use Test::More;
# generated by Dist::Zilla::Plugin::Test::PodSpelling bootstrapped version
eval "use Test::Spelling 0.12; use Pod::Wordlist::hanekomu; 1" or die $#;
add_stopwords(<DATA>);
all_pod_files_spelling_ok('bin', 'lib');
__DATA__
Fo
oer
bar
on all recent versions of perl this works fine. but in 5.8.8 the test fails. I found that by removing the ^ and $ the code works, its like Perls regex engine is ignoring the /m but the documentation says it was supported.
Why does this not work? and what is the most correct way to fix it? (note: I believe that the test should check that these elements are on a line by themselves )
This is bug RT#7781. It was fixed in 5.8.9 and 5.10.0.
Workarounds:
qr/^/m is equivalent to qr/(?:^|(?<=\n))/
qr/$/m is equivalent to qr/(?=\n|\z)/
code:
#!/usr/bin/env perl
use strict;
use warnings;
use locale;
my $prepinac_r = '';
my $array_name = '';
use Getopt::Long;
Getopt::Long::Configure ("bundling");
my $result = GetOptions(
"r=s" => \$prepinac_r,
"array-name=s" => \$array_name);
print STDERR "r: $prepinac_r\n";
print STDERR "array_name: $array_name\n";
running it:
script.pl --array-name=kokos -r=kure
output:
r: =kure
array_name: kokos
What I am doing wrong? What did I miss? Why -r gets "=kure" instead of "kure" ? Please help...
You're mixing short for and long form syntax. Short form syntax doesn't use = since that would make it less short.
"a|all" => \$opt_all,
"e|execute=s" => \$opt_execute,
Short form:
-aefoo
-a -efoo
-a -e foo
Long form:
--all --execute=foo
--all --execute foo
Here's an example of the short form you might be familiar with:
perl -le'print "Hello World";'
perl -l -e'print "Hello World";'
perl -l -e 'print "Hello World";'
Mr. Student, you should use double - for both switches >>
script.pl --array-name=coconut --r=chicken
...
r: chicken
array_name: coconut
I believe the behavior you are seeing is because of "bundling".
From Getopt::Long
Enabling this option will allow single-character options to be bundled. To distinguish bundles from long option names, long options must be introduced with -- and bundles with -
So if you are using "bundling", then -
--array-name=foo --r=bar # Works
-afoo -rbar # Also works
--array-name=foo -r=bar # Does not. as you've already seen
It also does not make sense to use bundling unless you are using options that do not require a parameter, and therefor can be "bundled"
Trying to run this little perl program from parsCit:
parsCit-client.pl e1.txt
Too late for -CSD option at [filename] line 1
e1.txt is here: http://dl.dropbox.com/u/10557283/parserProj/e1.txt
I'm running the program from win7 cmd, not Cygwin.
filename is parsCit-client.pl - entire program is here:
#!/usr/bin/perl -CSD
#
# Simple SOAP client for the ParsCit web service.
#
# Isaac Councill, 07/24/07
#
use strict;
use encoding 'utf8';
use utf8;
use SOAP::Lite +trace=>'debug';
use MIME::Base64;
use FindBin;
my $textFile = $ARGV[0];
my $repositoryID = $ARGV[1];
if (!defined $textFile || !defined $repositoryID) {
print "Usage: $0 textFile repositoryID\n".
"Specify \"LOCAL\" as repository if using local file system.\n";
exit;
}
my $wsdl = "$FindBin::Bin/../wsdl/ParsCit.wsdl";
my $parsCitService = SOAP::Lite
->service("file:$wsdl")
->on_fault(
sub {
my($soap, $res) = #_;
die ref $res ? $res->faultstring :
$soap->transport->status;
});
my ($citations, $citeFile, $bodyFile) =
$parsCitService->extractCitations($textFile, $repositoryID);
#print "$citations\n";
#print "CITEFILE: $citeFile\n";
#print "BODYFILE: $bodyFile\n";
From perldoc perlrun, about the -C switch:
Note: Since perl 5.10.1, if the -C option is used on the "#!" line, it
must be specified on the command line as well, since the standard
streams are already set up at this point in the execution of the perl
interpreter. You can also use binmode() to set the encoding of an I/O
stream.
Which is presumably what the compiler means by it being "too late".
In other words:
perl -CSD parsCit-client.pl
Because command-line options in a #! "shebang" are not passed consistently across all operating systems (see this answer), and Perl has already opened streams before parsing the script shebang, and so cannot compensate for this in some older OSs, it was decided in bug 34087 to forbid -C in the shebang. Of course, not everyone was happy with this "fix", particularly if it would have otherwise worked on their OS and they don't want to think about anything other than UTF-8.
If you think binmode() is ugly and unnecessary (and doesn't cover command-line arguments), you might like to consider the utf8::all package which has a similar effect to perl -CSDL.
Or were you using *nix, I would suggest export PERL_UNICODE="SDA" in the enclosing script to get Perl to realise it's in a UTF-8 environment.