perl: run another script without exec/system - perl

Is it possible to include/source another perl script, or launch it as a "sub"?
This works, but looks ugly:
test2.pl:
print "I'm in test2.pl; args: #ARGV\n";
test.pl:
#!/usr/bin/perl
use File::Temp qw/tempdir/;
use File::Copy qw/copy/;
my $tmplib;
use lib ($tmplib = tempdir()) . (
copy("./test2.pl", "$tmplib/test2.pm") ? "" : (die "$!")
);
use test2;
x
$ ./test.pl a b c
I'm in test2.pl; args: a b c

It sounds like you want the do operator, although it also sounds like very bad design.
This is what the documentation says.
do EXPR Uses the value of EXPR as a filename and executes the contents
of the file as a Perl script.
do 'stat.pl';
is just like
eval `cat stat.pl`;

You can use do to run another Perl script in the same interpreter:
do 'test2.pl';
This will reuse the command line parameters from the outer script. To pass different parameters, you can override #ARGV locally, like:
{
local #ARGV = qw(par1 par2 par3);
do 'test2.pl';
}

Related

Importing environment variables to Perl

I'm not sure if importing is the right word to use. I'm a beginner in both Perl and Bash. I have set a variable on Bash, so when I do:
echo $PRDIR
it prints a string (It's a directory name)
I want to import that string to Perl, and I don't know how to do that. I've tried:
$varex = system("$PRDIR");
print "$varex";
And also
$varex = system("echo $PRDIR");
print "$varex";
but that doesn't work (I understand the last one, It prints "0" because that's echo's return value). I've also tried redirecting stdout to a variable but I couldn't.
If you want Bash to export a variable into the environment so it's accessible to programs, you can use the export builtin:
export PRDIR
Inside Perl, you would then access it using the %ENV hash:
my $varex = $ENV{"PRDIR"};
print "\$varex is: $varex\n";
Another solution to use the variable directly in perl :
In the shell :
$ export PRDIR=foobar
In perl :
#!/usr/bin/perl
use Modern::Perl;
use Env qw/PRDIR/;
say $PRDIR;
I guess you need something like this:
use Cwd 'abs_path';
use File::Basename;
my $self = abs_path($0);
my $bindir = dirname( abs_path($0) );
unless ($ENV{APP_ENV}) {
warn "No APP_ENV, will try to get from bin/env.sh";
exec("source $bindir/env.sh && /usr/bin/perl $self") || die "$!";
}
I have env.sh in my bin folder with following content:
export APP_ENV=development
The idea behind this approach is that I don't need to bother if I set my ENV variables before running my Perl code or forget to do it. I need just to run my Perl program and it will take care about preparing environment for itself.

Can I explicitly specify $0 before running a Perl script?

I have a Perl wrapper script, wrapper, which exec's another tool after setting some environment variables. The tools which the wrapper may invoke are symlinked to wrapper, and it dynamically determines the tool which has symlinked to it by evaluating basename($0). Here's a contrived example to illustrate:
[/tmp]$ cat wrapper
#!/usr/bin/perl
use File::Basename;
$ENV{'CUSTOM_ENVIRONMENT'} = '1';
my $scriptName = basename($0);
exec("scripts/${scriptName}");
[/tmp]$ chmod +x wrapper
[/tmp]$ cat scripts/foo
#!/bin/sh
echo "foo"
[/tmp]$ ln -s wrapper foo
[/tmp]$ ./foo
foo
I would like to avoid needing the dummy foo symlink and explicitly set $0 before invoking wrapper directly. Is it possible to explicitly set $0 before invoking a Perl script?
I know I can assign to $0 from within wrapper, but I'd like to set the value of $0 to be used before I invoke wrapper, so I can spoof the file name that Perl thinks is being run.
I've scanned through perlrun and perlvar but haven't found anything.
Here's a possible hack:
perl -e '$0="something_else"; do "/path/to/wrapper"'
Here is a wrapper wrapper:
open (my $wrapper, "<", "wrapper") or die("$!");
my $c = '$0 = "SPOOF";'.join("", <$wrapper>);
close ($wrapper);
eval $c;

How to set breakpoint on a particular file in Perl program?

My Perl program looks like:
foo/
foo/bar/
for/bar/test.pm
foo.pm
foo/bar.pm
test.pl
and use perl test.pl to start the program.
I want to debug a sub in foo/bar/test.pm. How to set a breakpoint on that sub?
To debug a perl script, use the -d switch to invoke the debugger.
perl -d test.pl
Within the debugger you can use b <line no> to set a breakpoint in the current file. Sometimes it is a hassle to set a breakpoint in a file that hasn't been loaded yet or that was loaded a long time ago, so you can also put the line
$DB::single = 1;
anywhere in any perl program, and the debugger will break immediately after it executes that line. This is also a good way (the only way?) to set a breakpoint in code that will be run at compile time.
Just use the fully qualified name of the sub as the argument to b:
b foo::bar::test::subname
Example:
$ perl -d -e'use CGI; CGI->new'
...
main::(-e:1): use CGI; CGI->new
DB<1> b CGI::new
DB<2> r
CGI::new(.../CGI.pm:337):
337: my($class,#initializer) = #_;
DB<2> q
You can enter f Module.pm to tell the debugger to look at that particular module. Once you've done that, b line_number will stop at that line in Module.pm.
Once the original script has passed use Module, then b subroutine will stop at that subroutine. The only catch here is that you can't make your first two debugger commands f Module.pm; b subroutine because when the script begins, it hasn't passed use Module, so Module.pm hasn't loaded yet, which means that perl can't see that Module.pm has subroutine.

perl mktemp and echo

i am trying to put some word in tempfile via commandline
temp file creat but word not past in tempfile
#!/usr/bin/perl -w
system ('clear');
$TMPFILE = "mktemp /tmp/myfile/devid.XXXXXXXXXX";
$echo = "echo /"hello world/" >$TMPFILE";
system ("$TMPFILE");
system ("$echo");
Please Help to Solve This
To capture the name output by mktemp, do this instead:
chomp($TMPFILE = `mktemp /tmp/myfile/devid.XXXXXXXXXX`);
But Perl can do all the things you are doing without resorting to the shell.
Avoid using external commands from perl script as much as possible.
you can use: File::Temp module in this case, see this
Here's a specific demonstration of the advice that others have given you: where possible, use Perl directly rather than invoking system. Also, you should get in the habit of including use strict and use warnings in your Perl scripts.
use strict;
use warnings;
use File::Temp;
my $ft = File::Temp->new(
UNLINK => 0,
TEMPLATE => '/tmp/myfile/devid.XXXXXXXXXX',
);
print "Writing to temp file: ", $ft->filename, "\n";
print $ft "Hello, world.\n";

Can I run a Perl script from stdin?

Suppose I have a Perl script, namely mytest.pl. Can I run it by something like cat mytest.pl | perl -e?
The reason I want to do this is that I have a encrypted perl script and I can decrypt it in my c program and I want to run it in my c program. I don't want to write the decrypted script back to harddisk due to secruity concerns, so I need to run this perl script on-the-fly, all in memory.
This question has nothing to do with the cat command, I just want to know how to feed perl script to stdin, and let perl interpreter to run it.
perl < mytest.pl
should do the trick in any shell. It invokes perl and feeds the script in via the shell redirection operator <.
As pointed out, though, it seems a little unnecessary. Why not start the script with
#!/usr/bin/perl
or perhaps
#!/usr/bin/env perl
? (modified to reflect your Perl and/or env path)
Note the Useless Use of Cat Award. Whenever I use cat I stop and think whether the shell can provide this functionality for me instead.
Sometimes one needs to execute a perl script and pass it an argument. The STDIN construction perl input_file.txt < script.pl won't work. Using the tip from How to assign a heredoc value to a variable in Bash we overcome this by using a "here-script":
#!/bin/bash
read -r -d '' SCRIPT <<'EOS'
$total = 0;
while (<>) {
chomp;
#line = split "\t";
$total++;
}
print "Total: $total\n";
EOS
perl -e "$SCRIPT" input_file.txt
perl mytest.pl
should be the correct way. Why are you doing the unnecessary?
cat mytest.pl | perl
…is all you need. The -e switch expects the script as a command line argument.
perl will read the program from STDIN if you don't give it any arguments.
So you could theoretically read an encrypted file, decrypt it, and run it, without saving the file anywhere.
Here is a sample program:
#! /usr/bin/perl
use strict;
use warnings;
use 5.10.1;
use Crypt::CBC;
my $encrypted = do {
open my $encrypted_file, '<', 'perl_program.encrypted';
local $/ = undef;
<$encrypted_file>;
};
my $key = pack("H16", "0123456789ABCDEF");
my $cipher = Crypt::CBC->new(
'-key' => $key,
'-cipher' => 'Blowfish'
);
my $plaintext = $cipher->decrypt($encrypted);
use IPC::Run qw'run';
run [$^X], \$plaintext;
To test this program, I first ran this:
perl -MCrypt::CBC -e'
my $a = qq[print "Hello World\n"];
my $key = pack("H16", "0123456789ABCDEF");
my $cipher = Crypt::CBC->new(-key=>$key,-cipher=>"Blowfish");
my $encrypted = $cipher->encrypt($a);
print $encrypted;
' > perl_program.encrypted
This still won't stop dedicated hackers, but it will prevent most users from looking at the unencrypted program.