I am unable to evaluate 'modern Perl' code inside the Perl debugger. It works OK when debugging the code in a file, but not from the prompt.
Minimal example:
# Activating 5-10 features with -E (it works)
$ perl -E 'say "x"'
x
# Calling the debugger with -E
# It works for infile code, but for prompt line code...
$ perl -dEbug Loading DB routines from perl5db.pl version 1.33
DB say "x"
String found where operator expected at (eval 16)[/local-perl/lib/5.12.1/perl5db.pl:638] line 2, near "say "x""
at (eval 16)[/local-perl/lib/5.12.1/perl5db.pl:638] line 2
eval '($#, $!, $^E, $,, $/, $\\, $^W) = #saved;package main; $^D = $^D | $DB::db_stop;say "x";
(Note: the same happens with "use feature ':5.10'".)
Am I missing something?
I found a reference to the issue here, but it's about a year old. However, the relevant portion of the Perl source hasn't changed since and can be seen here. Essentially, if you take a look at toke.c in the Perl source, you see the following:
if (PL_perldb) {
/* Generate a string of Perl code to load the debugger.
* If PERL5DB is set, it will return the contents of that,
* otherwise a compile-time require of perl5db.pl. */
const char * const pdb = PerlEnv_getenv("PERL5DB");
...
}
...
if (PL_minus_E)
sv_catpvs(PL_linestr,
"use feature ':5." STRINGIFY(PERL_VERSION) "';");
Basically, the debugger is loaded before the -E flag is processed, so the features aren't yet enabled when the debugger gets loaded. The gist of this is that you can't currently use -E with the -d command. If you want to use say, switch, or any other feature from the debug prompt, you have to do it like this:
DB<1> use feature 'say'; say "x"
x
The closest I've seen to a solution is:
copy perl5db.pl from your PERL5LIB to either somewhere in PERL5LIB or the current directory, with a different name, say myperl5db.pl
2. Edit myperl5db.pl to have use feature ':5.10'; (or just 'state', or just 'say') on the first line.
3. Set the environment variable PERL5DB to "BEGIN { require 'myperl5db.pl' }"
Which I found at PerlMonks.
Related
I'm getting errors using CPAN and, as output is rather unhelpful, resorted to debugging this.
$ perl -d /usr/bin/cpan CPAN
Loading DB routines from perl5db.pl version 1.33
<...>
main::(/usr/bin/cpan:2): eval 'exec /usr/bin/perl -S $0 ${1+"$#"}'
main::(/usr/bin/cpan:3): if $running_under_some_shell;
DB<1> b /usr/lib/perl5/5.14/HTTP/Tiny.pm:125
DB<2> L
/usr/bin/cpan:
2: eval 'exec /usr/bin/perl -S $0 ${1+"$#"}'
break if (/usr/lib/perl5/5.14/HTTP/Tiny.pm:125)
DB<2> c
After c, the program runs to completion, ignoring the breakpoint.
/usr/lib/perl5/5.14/HTTP/Tiny.pm:125 is the following line, outside any conditional blocks.
my $request = {
method => $method,
scheme => $scheme,
host_port => ($port == $DefaultPort{$scheme} ? $host : "$host:$port"),
uri => $path_query,
headers => {},
};
Setting a break on line 142 (that's causing errors that I'm debugging) makes no difference.
From the L output, it looks like the breakpoint is set on the current line rather than the one I need. But, perldebug lists b file:line as valid syntax.
That syntax might be a newer addition, I wasn't able to get it working here either. I have 5.8.8 on Unix and 5.10.1 on Windows and both don't seem to work. I get the breakpoint on my current line as you do (it treats the whole string as a condition).
I'd suggest using another syntax as below.
Break on the method:
b HTTP::Tiny::some_method
Select the file, break on the line (it matches on paths, not module namespaces):
f HTTP/Tiny
b 125
If they are loaded at runtime with require or eval, the debugger won't know about the module yet so you can just type use HTTP::Tiny in the debugger first to force it to load.
#!/usr/bin/perl -sw
use strict;
use warnings;
use Getopt::Long;
my $remote = 0;
my $test = 0;
GetOptions ('remote' => \$remote, 'test' => \$test);
print "$remote:$test\n";
perl test.pl --remote --test
The above prints "0:0". I am new to Perl so I have been unable to determine why this isn't working.
I also ran the "Simple Options" section from http://perldoc.perl.org/Getopt/Long.html#Simple-options and that didn't produce anything either.
I believe the -s command line option you include on your she-bang line is biting you. According to the perlrun documentation, the -s command line option:
enables rudimentary switch parsing for switches on the command line after the program name but before any filename arguments (or before an argument of --).
If you remove that option, things should work as you expect. I would also recommend removing the -w since you are already using the use warnings directive (the use warnings directive is much more fully featured, essentially replacing the -w option).
So, long story short, make your first line:
#!/usr/bin/perl
Note that if running the script on Windows via cmd you must specify perl before the script name otherwise GetOptions doesn't work.
When I tried simply calling my script.pl on the command line without first putting perl the script ran but all the options weren't parsed.
I googled about #!/usr/bin/perl, but I could not find any satisfactory answer. I know it’s a pretty basic thing, but still, could explain me what is the significance of #!/usr/bin/perl in Perl? Moreover, what does -w or -T signify in #!/usr/bin/perl? I am a newbie to Perl, so please be patient.
The #! is commonly called a "shebang" and it tells the computer how to run a script. You'll also see lots of shell-scripts with #!/bin/sh or #!/bin/bash.
So, /usr/bin/perl is your Perl interpreter and it is run and given the file to execute.
The rest of the line are options for Perl. The "-T" is tainting (it means input is marked as "not trusted" until you check it's format). The "-w" turns warnings on.
You can find out more by running perldoc perlrun (perldoc is Perl's documentation reader, might be installed, might be in its own package).
For scripts you write I would recommend starting them with:
#!/usr/bin/perl
use warnings;
use strict;
This turns on lots of warnings and extra checks - especially useful while you are learning (I'm still learning and I've been using Perl for more than 10 years now).
Both -w and -T are sort of "foolproof" flags.
-w is the same as use warning statement in your code, and it's an equivalent of warning option in many compilers. A simplest example would be a warning about using uninitialized variable:
#!/usr/bin/perl -w
print "$A\n";
print "Hello, world!\n";
Will print:
Name "main::A" used only once: possible typo at ./perl-warnings line 3.
Use of uninitialized value $A in concatenation (.) or string at
./perl-warnings line 3.
Hello, world!
The -T flag means that any value that came from the outside world (as opposite to being calculated inside the program) is considered potential threat, and disallows usage of such values in system-related operations, like writing files, executing system command, etc. (That's why Perl would activate the "taint" mode when the script is running under setuid/setgid.)
The "tainted" mode is "enforcing" you to double-check the value inside the script.
E.g., the code:
#!/usr/bin/perl -T
$A = shift;
open FILE, ">$A";
print "$A\n";
close FILE;
Will produce a fatal error (terminating the program):
$ ./perl-tainted jkjk
Insecure dependency in open while running with -T switch at
./perl-tainted line 3.
And that's only because the argument value came from "outside" and was not "double-checked". The "taint" mode is drawing your attention to that fact. Of course, it's easy to fool it, e.g.:
#!/usr/bin/perl -T
$A = shift;
$A = $1 if $A =~ /(^.*$)/;
open FILE, ">$A";
print "$A\n";
close FILE;
In this case everything worked fine. You "fooled" the "taint mode". Well, the assumption is that programer's intentions are to make the program safer, so the programmer wouldn't just work around the error, but would rather take some security measures. One of Perl's nicknames is "the glue and the duct tape of system administrators". It's not unlikely that system administrator would create Perl script for his own needs and would run it with root permissions. Think of this script doing something normal users are not allowed to do... you probably want to double-check things which are not part of the program itself, and you want Perl to remind you about them.
Hope it helps.
about Taint Mode(-T):
require and use statements change when taint mode is turned on.
The path to load libraries/modules no longer contains . (the current directory) from its path.
So if you load any libraries or modules relative to the current working directory without explicitly specifying the path, your script will break under taint mode.
For ex: Consider perl_taint_ex.pl
#!/usr/bin/perl -T
require "abc.pl";
print "Done";
would fail like this
D:\perlex>perl perl_taint_ex.pl
"-T" is on the #! line, it must also be used on the command line
at perl_taint_ex.pl line 1.
D:\perlex>perl -T perl_taint_ex.pl
Can't locate abc.pl in #INC (#INC contains: C:/Perl/site/lib C:/Perl/lib)
at perl_taint_ex.pl line 3.
So when taint mode is on, you must tell the require statement explicitly where to load the library since . is removed during taint mode from the #INC array.
#INC contains a list of valid paths to read library files and modules from.
If taint mode is on, you would simply do the following:
D:\perlex>perl -ID:\perlex -T perl_taint_ex.pl
Done
-ID:\perlex will include directory D:\perlex in #INC.
You can try other ways for adding path to #INC,this is just one example.
It's called a shebang. On Unix based systems (OSX, Linux, etc...) that line indicates the path to the language interpreter when the script is run from the command line. In the case of perl /usr/bin/perl is the path to the perl interpreter. If the hashbang is left out the *nix systems won't know how to parse the script when invoked as an executable. It will instead try to interpret the script in whatever shell the user happens to be running (probably bash) and break the script.
http://en.wikipedia.org/wiki/Hashbang
The -W and -T are arguments that controll the way the perl interpreter operates. They are the same arguments that you could invoke when calling perl interpreter directly from the command line.
-W shows warnings (aka debuging information).
-T turns on taint / security checking.
I know this is incorrect. I just want to know how perl parses this.
So, I'm playing around with perl, what I wanted was perl -ne what I typed was perl -ie the behavior was kind of interesting, and I'd like to know what happened.
$ echo 1 | perl -ie'next unless /g/i'
So perl Aborted (core dumped) on that. Reading perl --help I see -i takes an extension for backups.
-i[extension] edit <> files in place (makes backup if extension supplied)
For those that don't know -e is just eval. So I'm thinking one of three things could have happened either it was parsed as
perl -i -e'next unless /g/i' i gets undef, the rest goes as argument to e
perl -ie 'next unless /g/i' i gets the argument e, the rest is hanging like a file name
perl -i"-e'next unless /g/i'" whole thing as an argument to i
When I run
$ echo 1 | perl -i -e'next unless /g/i'
The program doesn't abort. This leads me to believe that 'next unless /g/i' is not being parsed as a literal argument to -e. Unambiguously the above would be parsed that way and it has a different result.
So what is it? Well playing around with a little more, I got
$ echo 1 | perl -ie'foo bar'
Unrecognized switch: -bar (-h will show valid options).
$ echo 1 | perl -ie'foo w w w'
... works fine guess it reads it as `perl -ie'foo' -w -w -w`
Playing around with the above, I try this...
$ echo 1 | perl -ie'foo e eval q[warn "bar"]'
bar at (eval 1) line 1.
Now I'm really confused.. So how is Perl parsing this? Lastly, it seems you can actually get a Perl eval command from within just -i. Does this have security implications?
$ perl -i'foo e eval "warn q[bar]" '
Quick answer
Shell quote-processing is collapsing and concatenating what it thinks is all one argument. Your invocation is equivalent to
$ perl '-ienext unless /g/i'
It aborts immediately because perl parses this argument as containing -u, which triggers a core dump where execution of your code would begin. This is an old feature that was once used for creating pseudo-executables, but it is vestigial in nature these days.
What appears to be a call to eval is the misparse of -e 'ss /g/i'.
First clue
B::Deparse can your friend, provided you happen to be running on a system without dump support.
$ echo 1 | perl -MO=Deparse,-p -ie'next unless /g/i'
dump is not supported.
BEGIN { $^I = "enext"; }
BEGIN { $/ = "\n"; $\ = "\n"; }
LINE: while (defined(($_ = <ARGV>))) {
chomp($_);
(('ss' / 'g') / 'i');
}
So why does unle disappear? If you’re running Linux, you may not have even gotten as far as I did. The output above is from Perl on Cygwin, and the error about dump being unsupported is a clue.
Next clue
Of note from the perlrun documentation:
-u
This switch causes Perl to dump core after compiling your program. You can then in theory take this core dump and turn it into an executable file by using the undump program (not supplied). This speeds startup at the expense of some disk space (which you can minimize by stripping the executable). (Still, a "hello world" executable comes out to about 200K on my machine.) If you want to execute a portion of your program before dumping, use the dump operator instead. Note: availability of undump is platform specific and may not be available for a specific port of Perl.
Working hypothesis and confirmation
Perl’s argument processing sees the entire chunk as a single cluster of options because it begins with a dash. The -i option consumes the next word (enext), as we can see in the implementation for -i processing.
case 'i':
Safefree(PL_inplace);
[Cygwin-specific code elided -geb]
{
const char * const start = ++s;
while (*s && !isSPACE(*s))
++s;
PL_inplace = savepvn(start, s - start);
}
if (*s) {
++s;
if (*s == '-') /* Additional switches on #! line. */
s++;
}
return s;
For the backup file’s extension, the code above from perl.c consumes up to the first whitespace character or end-of-string, whichever is first. If characters remain, the first must be whitespace, then skip it, and if the next is a dash then skip it also. In Perl, you might write this logic as
if ($$s =~ s/i(\S+)(?:\s-)//) {
my $extension = $1;
return $extension;
}
Then, all of -u, -n, -l, and -e are valid Perl options, so argument processing eats them and leaves the nonsensical
ss /g/i
as the argument to -e, which perl parses as a series of divisions. But before execution can even begin, the archaic -u causes perl to dump core.
Unintended behavior
An even stranger bit is if you put two spaces between next and unless
$ perl -ie'next unless /g/i'
the program attempts to run. Back in the main option-processing loop we see
case '*':
case ' ':
while( *s == ' ' )
++s;
if (s[0] == '-') /* Additional switches on #! line. */
return s+1;
break;
The extra space terminates option parsing for that argument. Witness:
$ perl -ie'next nonsense -garbage --foo' -e die
Died at -e line 1.
but without the extra space we see
$ perl -ie'next nonsense -garbage --foo' -e die
Unrecognized switch: -onsense -garbage --foo (-h will show valid options).
With an extra space and dash, however,
$ perl -ie'next -unless /g/i'
dump is not supported.
Design motivation
As the comments indicate, the logic is there for the sake of harsh shebang (#!) line constraints, which perl does its best to work around.
Interpreter scripts
An interpreter script is a text file that has execute permission enabled and whose first line is of the form:
#! interpreter [optional-arg]
The interpreter must be a valid pathname for an executable which is not itself a script. If the filename argument of execve specifies an interpreter script, then interpreter will be invoked with the following arguments:
interpreter [optional-arg] filename arg...
where arg... is the series of words pointed to by the argv argument of execve.
For portable use, optional-arg should either be absent, or be specified as a single word (i.e., it should not contain white space) …
Three things to know:
'-x y' means -xy to Perl (for some arbitrary options "x" and "y").
-xy, as common for unix tools, is a "bundle" representing -x -y.
-i, like -e absorbs the rest of the argument. Unlike -e, it considers a space to be the end of the argument (as per #1 above).
That means
-ie'next unless /g/i'
which is just a fancy way of writing
'-ienext unless /g/i'
unbundles to
-ienext -u -n -l '-ess /g/i'
^^^^^ ^^^^^^^
---------- ----------
val for -i val for -e
perlrun documents -u as:
This switch causes Perl to dump core after compiling your program. You can then in theory take this core dump and turn it into an executable file by using the undump program (not supplied). This speeds startup at the expense of some disk space (which you can minimize by stripping the executable). (Still, a "hello world" executable comes out to about 200K on my machine.) If you want to execute a portion of your program before dumping, use the dump() operator instead. Note: availability of undump is platform specific and may not be available for a specific port of Perl.
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.