Whenever I start the perl debugger from a script with a -d option, the session starts with emacs command line editing. I then type ESC ctrl J to enable vi editing. I want to enable vi from the get-go.
I tried using the following .perldb:
&parse_options("HistFile=.perlDebugHist");
sub afterinit { push #DB::typeahead, ("o inhibit_exit", chr(27).chr(10)) }
but when the session starts, it says
auto(-2) DB<62> o inhibit_exit
inhibit_exit = '1'
auto(-1) DB<63>
Unrecognized character \x1B; marked by <-- HERE after :db_stop;
<-- HERE near column 96 at (eval 9)[/usr/share/perl/5.22/perl5db.pl:737] line 2.
at (eval 9)[/usr/share/perl/5.22/perl5db.pl:737] line 2.
eval 'no strict; ($#, $!, $^E, $,, $/, $\\, $^W) = #DB::saved;package main; $^D = $^D | $DB::db_stop;
;
' called at /usr/share/perl/5.22/perl5db.pl line 737
DB::eval called at /usr/share/perl/5.22/perl5db.pl line 3110
DB::DB called at ~/bin/debug.pl line 61
Here is a possible workaround that assumes you use the gnu readline library:
Create a file called perldb_inputrc in the current directory with content:
set editing-mode vi
Then change the afterinit() sub to:
sub afterinit {
if (!$DB::term) {
DB::setterm();
}
$DB::term->read_init_file('perldb_inputrc');
push #DB::typeahead, "o inhibit_exit";
}
See perldoc perl5db for more information.
Update:
A simpler approach is to the readline init file. You can use a global file ~/.inputrc or a use a local one for the current debugging session only by setting the environment variable INPUTRC. For example, using the above perldb_inputrc file as an example, you could use (in your .perldb init file):
sub afterinit { push #DB::typeahead, "o inhibit_exit" }
and then run the Perl script like this:
INPUTRC=./perldb_inputrc perl -d myscript.pl
Related
My .vimrc is configured to automatically compile my Perl script upon save.
However, it is hard-coded to perl 5.14.
Some of the scripts I maintain are 5.8.8, and others are 5.16
I would like vim to compile my code based on the version in the hashbang #! line
Is this possible?
Here is my current .vimrc:
" check perl code with :make
" by default it will call "perl -c" to verify the code and, if there are any errors, the cursor will be positioned in the offending line.
" errorformat uses scanf to extract info from errors/warnings generated by make.
" %f = filename %l = line number %m = error message
" autowrite tells vim to save the file whenever the buffer is changed (for example during a save)
" BufWritePost executes the command after writing the buffer to file
" [F4] for quick :make
autocmd FileType perl set makeprg=/home/utils/perl-5.14/5.14.1-nothreads-64/bin/perl\ -c\ %\ $*
autocmd FileType perl set errorformat=%f:%l:%m
autocmd FileType perl set autowrite
autocmd BufWritePost *.pl,*.pm,*.t :make
You have to dynamically change the Perl path in 'makeprg' based on the shebang line. For example:
:let perlExecutable = matchstr(getline(1), '^#!\zs\S\+')
:let perlExecutable = (empty(perlExecutable) ? '/home/utils/perl-5.14/5.14.1-nothreads-64/bin/perl' : perlExecutable) " Default in case of no match
:let &makeprg = perlExecutable . ' -c % $*'
You can invoke that (maybe encapsulated in a function) on the FileType event, but then it won't properly detect new files that don't have the shebang yet. Or prepend the call to your autocmd BufWritePost *.pl,*.pm,*.t :make; that will re-detect after each save.
PS: Instead of all those :autocmd FileType in your ~/.vimrc, I would rather place those in ~/.vim/after/ftplugin/perl.vim (if you have :filetype plugin on). Also, you should use :setlocal instead of (global) :set.
Use plenv to change perl versions for your projects: plenv local 5.8.8
https://github.com/tokuhirom/plenv
You don't have to specify the path for shebang and makeprg.
I found another way to solve this problem.
Instead of setting makeprg to the perl executable, I tell vim to execute another script.
That script determines the appropriate version of perl, then runs that version with -c
This solution could be extended for other scripting languages
In the .vimrc, change:
autocmd FileType perl set makeprg=/home/utils/perl-5.14/5.14.1-nothreads-64/bin/perl\ -c\ %\ $*
to:
autocmd FileType perl set makeprg=/home/username/check_script_syntax.pl\ %\ $*
#!/home/utils/perl-5.14/5.14.1-nothreads-64/bin/perl
use 5.014; # enables 'say' and 'strict'
use warnings FATAL => 'all';
use IO::All;
my $script = shift;
my $executable;
for my $line ( io($script)->slurp ) {
##!/home/utils/perl-5.14/5.14.1-nothreads-64/bin/perl
##!/home/utils/perl-5.8.8/bin/perl
if ( $line =~ /^#!(\/\S+)/ ) {
$executable = $1;
} elsif ( $script =~ /\.(pm)$/ ) {
if ( $script =~ /(releasePatterns.pm|p4Add.pm|dftform.pm)$/ ) {
$executable = '/home/utils/perl-5.8.8/bin/perl';
} else {
$executable = '/home/utils/perl-5.14/5.14.1-nothreads-64/bin/perl';
}
} else {
die "ERROR: Could not find #! line in your script $script";
}
last;
}
if ( $script =~ /\.(pl|pm|t)$/ ) {
$executable .= " -c";
} else {
die "ERROR: Did not understand how to compile your script $script";
}
my $cmd = "$executable $script";
say "Running $cmd";
say `$cmd`;
I need to use sys/ioctl.ph and it's not included in the perl version (5.12.3) shipped with my slackware distribution.
I did the following:
cd /usr/include
h2ph sys/ioctl.ph
cd /usr/lib64/perl5/site_perl/5.12.3/x86_64-linux-thread-multi/
mkdir sys
mv ioctl.ph sys
Now the perl interpreter doesn't complain about the sys/ioctl.ph, but this is the error I get:
Illegal declaration of subroutine Functions::ServerSocket::__INT16_C at /usr/lib64/perl5/site_perl/5.12.3/x86_64-linux-thread-multi/_h2ph_pre.ph line 164.
This is what there's in the file that causes the error at line 164:
unless (defined &__INT16_C(c)) { sub __INT16_C(c)() { &c } }
I don't know where to start. Functions::ServerSocket is one of my module, but I don't have any function like that in my file.
The __INT16_C macro on your platform is probably simple, e.g.,
#define __INT16_C(c) c
Replace the code on line 164 with
eval 'sub __INT16_C {
my($c) = #_;
eval q($c);
}' unless defined (&__INT16_C);
which is what other versions of h2ph generate.
I want to call "env.sh " from "my_perl.pl" without forking a subshell. I tried with backtics and system like this --> system (. env.sh) [dot space env.sh] , however wont work.
Child environments cannot change parent environments. Your best bet is to parse env.sh from inside the Perl code and set the variables in %ENV:
#!/usr/bin/perl
use strict;
use warnings;
sub source {
my $name = shift;
open my $fh, "<", $name
or die "could not open $name: $!";
while (<$fh>) {
chomp;
my ($k, $v) = split /=/, $_, 2;
$v =~ s/^(['"])(.*)\1/$2/; #' fix highlighter
$v =~ s/\$([a-zA-Z]\w*)/$ENV{$1}/g;
$v =~ s/`(.*?)`/`$1`/ge; #dangerous
$ENV{$k} = $v;
}
}
source "env.sh";
for my $k (qw/foo bar baz quux/) {
print "$k => $ENV{$k}\n";
}
Given
foo=5
bar=10
baz="$foo$bar"
quux=`date +%Y%m%d`
it prints
foo => 5
bar => 10
baz => 510
quux => 20110726
The code can only handle simple files (for instance, it doesn't handle if statements or foo=$(date)). If you need something more complex, then writing a wrapper for your Perl script that sources env.sh first is the right way to go (it is also probably the right way to go in the first place).
Another reason to source env.sh before executing the Perl script is that setting the environment variables in Perl may happen too late for modules that are expecting to see them.
In the file foo:
#!/bin/bash
source env.sh
exec foo.real
where foo.real is your Perl script.
You can use arbitrarily complex shell scripts by executing them with the relevant shell, dumping their environment to standard output in the same process, and parsing that in perl. Feeding the output into something other than %ENV or filtering for specific values of interest is prudent so you don't change things like PATH that may have interesting side effects elsewhere. I've discarded standard output and error from the spawned shell script although they could be redirected to temporary files and used for diagnostic output in the perl script.
foo.pl:
#!/usr/bin/perl
open SOURCE, "bash -c '. foo.sh >& /dev/null; env'|" or
die "Can't fork: $!";
while(<SOURCE>) {
if (/^(BAR|BAZ)=(.*)/) {
$ENV{$1} = ${2} ;
}
}
close SOURCE;
print $ENV{'BAR'} . "\n";
foo.sh:
export BAR=baz
Try this (unix code sample):
cd /tmp
vi s
#!/bin/bash
export blah=test
vi t
#!/usr/bin/perl
if ($ARGV[0]) {
print "ENV second call is : $ENV{blah}\n";
} else {
print "ENV first call is : $ENV{blah}\n";
exec(". /tmp/s; /tmp/t 1");
}
chmod 777 s t
./t
ENV first call is :
ENV second call is : test
The trick is using the exec to source your bash script first and then calling your perl script again with an argument so u know that you are being called for a second time.
I use a Perl(loader.vim) script to load VIM modules: (.vimrc) source /whatever/loader.vim
loader.vim:
function! LoadBundles()
perl HERE
while(</root/.vim/bundle/*/plugin/*>) {
my ($path, $fname) =($_ =~ m|^(.+/)(.+?)$|);
#VIM::Msg("$path $fname\n");
VIM::DoCommand("set runtimepath=$path");
VIM::DoCommand("runtime! $fname");
}
HERE
endfunction
call LoadBundles()
I'd like to do something like LoadBundles('/path/to/bundledir') but to do this I need to be able to read a variable from within Perl eg:
function! LoadBundles(path)
let var = a:path
perl HERE
print "$var\n";
How do I do this???
I'd also like to save the runtimepath within perl HERE and then restore it. How do I read runtimepath from within perl HERE?
Here's how you can get at the "runtimepath" option from embedded Perl:
perl VIM::Msg( VIM::Eval('&runtimepath') )
Do the following to get more from the docs:
:help if_perl.txt
Then search for "VIM::Eval". So try:
function! AnExample(arg)
perl << EOF
VIM::Msg( VIM::Eval('a:arg') )
EOF
endfunction
And then to test:
:so %
:call AnExample("hello")
In Perl, you can execute system commands using system() or `` (backticks). You can even capture the output of the command into a variable. However, this hides the program execution in the background so that the person executing your script can't see it.
Normally this is useful but sometimes I want to see what is going on behind the scenes. How do you make it so the commands executed are printed to the terminal, and those programs' output printed to the terminal? This would be the .bat equivalent of "#echo on".
I don't know of any default way to do this, but you can define a subroutine to do it for you:
sub execute {
my $cmd = shift;
print "$cmd\n";
system($cmd);
}
my $cmd = $ARGV[0];
execute($cmd);
And then see it in action:
pbook:~/foo rudd$ perl foo.pl ls
ls
file1 file2 foo.pl
As I understand, system() will print the result of the command, but not assign it. Eg.
[daniel#tux /]$ perl -e '$ls = system("ls"); print "Result: $ls\n"'
bin dev home lost+found misc net proc sbin srv System tools var
boot etc lib media mnt opt root selinux sys tmp usr
Result: 0
Backticks will capture the output of the command and not print it:
[daniel#tux /]$ perl -e '$ls = `ls`; print "Result: $ls\n"'
Result: bin
boot
dev
etc
home
lib
etc...
Update: If you want to print the name of the command being system() 'd as well, I think Rudd's approach is good. Repeated here for consolidation:
sub execute {
my $cmd = shift;
print "$cmd\n";
system($cmd);
}
my $cmd = $ARGV[0];
execute($cmd);
Use open instead. Then you can capture the output of the command.
open(LS,"|ls");
print LS;
Here's an updated execute that will print the results and return them:
sub execute {
my $cmd = shift;
print "$cmd\n";
my $ret = `$cmd`;
print $ret;
return $ret;
}
Hmm, interesting how different people are answering this different ways. It looks to me like mk and Daniel Fone interpreted it as wanting to see/manipulate the stdout of the command (neither of their solutions capture stderr fwiw). I think Rudd got closer. One twist you could make on Rudd's response is to overwite the built in system() command with your own version so that you wouldn't have to rewrite existing code to use his execute() command.
using his execute() sub from Rudd's post, you could have something like this at the top of your code:
if ($DEBUG) {
*{"CORE::GLOBAL::system"} = \&{"main::execute"};
}
I think that will work but I have to admit this is voodoo and it's been a while since I wrote this code. Here's the code I wrote years ago to intercept system calls on a local (calling namespace) or global level at module load time:
# importing into either the calling or global namespace _must_ be
# done from import(). Doing it elsewhere will not have desired results.
delete($opts{handle_system});
if ($do_system) {
if ($do_system eq 'local') {
*{"$callpkg\::system"} = \&{"$_package\::system"};
} else {
*{"CORE::GLOBAL::system"} = \&{"$_package\::system"};
}
}
Another technique to combine with the others mentioned in the answers is to use the tee command. For example:
open(F, "ls | tee /dev/tty |");
while (<F>) {
print length($_), "\n";
}
close(F);
This will both print out the files in the current directory (as a consequence of tee /dev/tty) and also print out the length of each filename read.