Is there a way to get the Perl interpreter to list all the global variables in a pl file? - perl

I have a huge system that I'm converting from a cgi to a daemon and I need to find all the variables that end up being global (as in not declared with my $...)
They are probably intended to be scoped locally, but in perl if you accidentally forget, it doesn't care, but now I do.
I gotta figure the perl interpreter can tell the difference, so is there a way to get it to tell me? I could go through all the code by hand, looking for declarations, but there thousands and thousands and thousands of lines of code in this system.

perldoc strict will show all variables not declared with my:
$ perl -Mstrict=vars -c -e '$x=5; my $y=7; $z=6;'
Global symbol "$x" requires explicit package name at -e line 1.
Global symbol "$z" requires explicit package name at -e line 1.
-e had compilation errors.
The same thing with a list of files:
$ perl -Mstrict=vars -c *.pl
Here is another way using perldoc B::Xref
$ perl -MO=Xref -e '$x=5; my $y=7; $z=6;'
... lots of verbose output
Subroutine (main)
Package (lexical)
$y i1
Package main
$x 1
$z 1

With the PadWalker Module you can see all variables in a specific scope. You can see variables declared with "my" and "our".
You should still use "use strict" but using "use strict" does not show you global variables that you declare as global.
For example this example still has a global variable and strict does not do anything
#!/usr/bin/env perl
use strict;
use warnings;
our $foo;
With PadWalker you can use something like this.
#!/usr/bin/env perl
use strict;
use warnings;
use DDP;
use PadWalker qw(peek_our);
our $var;
print p peek_our(0);
The output is
\ {
$var \ undef
}
Sure, you only should use PadWalker for Debugging.

Related

Perl state variable requires explicit package name

I am having problems getting state variables to work. I have extensive experience with "my" variables, but getting "state" variables to work hasn't been easy.
Here is a bare minimum example to reproduce the problem:
$ perl -e 'use strict; sub test {state $string = ""; print $string; }'
Global symbol "$string" requires explicit package name (did you forget to declare "my $string"?) at -e line 1.
Global symbol "$string" requires explicit package name (did you forget to declare "my $string"?) at -e line 1.
Execution of -e aborted due to compilation errors.
$ perl --version
This is perl 5, version 32, subversion 1 (v5.32.1) built for MSWin32-x64-multi-thread
According to the documentation for the state keyword:
state is available only if the "state" feature is enabled or if it is
prefixed with CORE::. The "state" feature is enabled automatically
with a use v5.10 (or higher) declaration in the current scope.
Here is one way to change your one-liner to avoid the error:
$ perl -e 'use strict; use feature "state"; sub test {state $string = ""; print $string; }'
The docs mention:
This feature is available starting with Perl 5.10.
Although you are using Perl version v5.32.1, which is later than 5.10, it is still necessary to explicitly enable the state feature using one of the methods shown in the documentation. This feature is currently not enabled by default for any version of Perl.

In Perl, why do I need Exporter?

I have a module called hsfSubs.pm in my perl\lib folder. I have nothing in the module but subroutines and 1; at the end.
One subroutine, for example, is named pause. I have implemented no import or export routines.
In my main programs, I simply say use hsfSubs;, and I am subsequently able to call pause with no problem. Ditto if I say use hsfSubs qw(pause);.
Why would I need to use Exporter, #EXPORT and #EXPORT_OK, etc. or any other complications?
The multiple answers on Stack Overflow to questions about Exporter tell how to use it, but I fail to see why to use it.
The short version is that you wanted a module, but you ended up with what this calls a library. These aren't good because they pollute their caller's namespace (which can cause plenty of problems). But more crucially here, loading them using require or use (as oppose to do) is buggy.
If it had been properly written as a module, your example would not work. Exporter is the solution to that problem.
Let's dive into the details.
Like I said, there's a problem with your module. As you've noticed, it sometimes works despite the bug.
$ cat Buggy.pm
sub test { "ok!" }
1;
$ perl -e'use Buggy; CORE::say(test());'
ok!
But that's just because your example is too simple. Let's add a properly-written[1] module to the mix.
$ cat Buggy.pm
sub test { "ok!" }
1;
$ cat Other.pm
package Other;
use Buggy;
1;
$ perl -e'use Other; use Buggy; CORE::say(test());'
Undefined subroutine &main::test called at -e line 1.
The bug in your module is that it doesn't have a package directive. Modules loaded using use and require must always use a package directive. But as soon as you add that, your module stops working.
$ cat NotBuggy.pm
package NotBuggy;
sub test { "ok!" }
1;
$ perl -e'use NotBuggy; CORE::say(test());'
Undefined subroutine &main::test called at -e line 1.
Exporter is used to solve that problem.
$ cat Final.pm
package Final;
use Exporter qw( import );
our #EXPORT = qw( test );
sub test { "ok!" }
1;
$ perl -e'use Final; CORE::say(test());'
ok!
Well, not really. If it was properly written, it would include use use strict; use warnings 'all';. Always include that! It was omitted here to keep things visually simple.

Perl: Capture incorrect subroutine module names at check syntax? [duplicate]

This question already has answers here:
How do I get perl -c to throw Undefined or Undeclared function errors?
(4 answers)
Closed 8 years ago.
If I have a perl module such as:
package mytest;
use base 'Exporter';
our #EXPORT = qw(mysub);
sub mysub
{
print "foo";
}
1;
How can I use perl -c (syntax check) to capture when the code has typos in the function names? Currently I have tried this:
use strict;
use warnings;
use mytest;
&mysub();
&mysup(); # <-- Run time crash that is not captured with perl -c myfile.pl
mysup(); # <-- Also not captured with perl -c myfile.pl
You can't do that.
The -c option checks only that the syntax of the Perl file is correct. A call of a non-existent subroutine is a semantic error, and because a Perl subroutine can be defined at any point during run time, it can be diagnosed only when an attempt is made to call a subroutine.
Incidentally, global identifiers like package names should be capitalised, so your package should be called Mytest and the source should be in Mytest.pm.
Also, it is bad practice to call subroutines using the ampersand & prefix. Your main program should look like this
use strict;
use warnings;
use Mytest;
mysub();
mysup();
mysup();

Is there a way to enable/disable Smart::Comments via a command line switch in my Perl program?

I'd like to enable/disable the comments within my Perl program that make use of the module Smart::Comments. I've toyed with the idea of doing this by providing a --verbose switch as part of my list of command line options. When this switch is set, I was thinking of enabling the Smart::Comment module like so:
#!/usr/bin/perl
use Getopt::Long;
use Smart::Comments;
my $verbose = 0;
GetOptions ('verbose' => \$verbose);
if (! $verbose) {
eval "no Smart::Comments";
}
### verbose state: $verbose
However this doesn't work for me. It seems to be something with the way Smart::Comments itself works, so I'm suspicious of the way in which I'm trying to disable the module with the eval "no ..." bit. Can anyone offer me some guidance on this?
Take out the use Smart::Comments line out of the script, and run you script with or without the -MSmart::Comments option. Using the -M<module> option is like putting a use <module> statement at the beginning of your script.
# Smart comments off
$ perl my_script.pl
# Smart comments on
$ perl -MSmart::Comments my_script.pl ...
Also see the $ENV{Smart_Comments} variable in the Smart::Comments docs.
Here, you would use Smart::Comments in your script like
use Smart::Comments -ENV;
and then run
$ perl my_script.pl
$ Smart_Comments=0 perl my_script.pl
to run without smart comments, and
$ Smart_Comments=1 perl my_script.pl
to run with smart comments.
Update The Smart::Comments module is a source filter. Trying to turn it on and off at runtime (e.g., eval "no Smart::Comments") won't work. At best, you can do some configuration at compile time (say, in a BEGIN{} block, before loading Smart::Comments):
use strict;
use warnings;
BEGIN { $ENV{Smart_Comments} = " #ARGV " =~ / --verbose / }
use Smart::Comments -ENV;
...
Use "if" pragma:
use if !$ENV{MY_APP_NDEBUG}, 'Smart::Comments';
# or
use if $ENV{MY_APP_DEBUG}, 'Smart::Comments';
This doesn't load Smart::Comments if not required.

Can I use perl's switches with /bin/env in the shebang line?

I want to run perl -w using env. That works fine on the command line:
$ /bin/env perl -we 'print "Hello, world!\n"'
Hello, world!
But it doesn't work on the shebang line in a script:
#!/bin/env perl -w
print "Hello, world!\n";
Here is the error:
/bin/env: perl -w: No such file or directory
Apparently env doesn't understand the -w flag that I'm passing to perl. What's wrong?
The hash-bang isn't a normal shell command-line, the parsing and white-space handling is different - that's what you've hit. See:
http://www.in-ulm.de/~mascheck/various/shebang/
http://homepages.cwi.nl/~aeb/std/hashexclam-1.html#ss1.3
http://en.wikipedia.org/wiki/Shebang_(Unix)
Basically many/most unixes put all of the remaining text after the first space into a single argument.
So:
#!/bin/env perl -w
is the equivalent of:
/bin/env "perl -w"
so you need to handle any options to the perl interpreter in some other fashion. i.e.
use warnings;
(as #Telemachus)
Instead of -w use the warnings pragma (for modern versions of Perl):
#!/bin/env perl
use warnings;
use strict;
I thought it might be useful to bring up that "-w" is not the same as "use warnings". -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"
It's worth noting that Mac OS X interprets characters after the shebang as arguments, so on OS X, the following will work:
#!/usr/bin/env perl -wT
enter code here
However, since one of the points of using #!/usr/bin/env is to foster cross-platform compatibility, it's probably best not to use that syntax even if you're on a Mac mostly.