Capturing output from Perl's require - perl

Is it possible to capture output from Perl's require?
For example:
{
local #ARGV = qw/ hello world /;
require 'myscript.pl';
}
Id like to capture any stdout that myscript.pl generates. Can imagine something like this:
{
local #ARGV = qw/ hello world /;
my $output = require 'myscript.pl';
}

Capture::Tiny makes this easier:
use Capture::Tiny 'capture_stdout';
my $output = capture_stdout {
local #ARGV = qw/hello world/;
require 'foo.pl';
};
although I would agree that this is generally not a good way to run a script.

Yes, it's possible. You need to redirect STDOUT before requireing and restore the original STDOUT afterwards.
a.pl
my $capture;
open STDOUTBACKUP, '>&STDOUT';
close STDOUT;
open STDOUT, '>', \$capture;
require 'b.pl';
close STDOUT;
open STDOUT, '>&STDOUTBACKUP';
print "CAPTURED: $capture";
b.pl
print "ModuleB";
Output is CAPTURED: ModuleB

myscript.pl appears to be a Perl script. It makes no sense to use require or do.
use String::ShellQuote qw( shell_quote );
my $cmd = shell_quote('myscript.pl', 'hello', 'world');
my $output = `$cmd`;
die("Can't execute myscript.pl: $!\n") if $? == -1;
die("myscript.pl killed by signal ".( $? & 0x7F )."\n") if $? & 0x7F;
die("myscript.pl returned error ".( $? >> 8 )."\n") if $? >> 8;
or
open(my $pipe, '-|', 'myscript.pl', 'hello', 'world')
or die("Can't execute myscript.pl: $!\n");
my $output = '';
$output .= $_ while <$pipe>;
close($pipe);
die("myscript.pl killed by signal ".( $? & 0x7F )."\n") if $? & 0x7F;
die("myscript.pl returned error ".( $? >> 8 )."\n") if $? >> 8;

Related

can open command in perl execute .bat script?

I've this subroutine whitch start a .bat script but I don't see which line do that.
sub Traitement_Proc {
foreach my $input (#_) {
my $cmd = Fonctions_communes::ResolvEnvDos($input);
my ($proc, $args);
my $chExec;
if ($cmd =~ /^\"/) {
($proc, $args) = $cmd =~ /^\"(.+?)\"(.*)/;
} else {
($proc, $args) = $cmd =~ /^([^\s]+)(.*)/;
}
$chExec = File::Spec->catfile($::G_ParamTable{$::cstExecDir}, $proc);
$chExec = File::Spec->rel2abs($chExec, File::Spec->curdir());
$chExec = "\"".$chExec."\"" . $args;
Fonctions_communes::PrintError(" PROC : "._("Execution of script")." <" . $chExec . ">");
open PROC_OUT, $chExec." 2>&1"." & if ERRORLEVEL 1 exit/b 1"." |";
while(my $output = <PROC_OUT>) {
chomp($output);
Fonctions_communes::PrintError(decode($Fonctions_communes::console_encoding,$output));
}
close(PROC_OUT);
if ($? == 1) {
Fonctions_communes::PrintError(_("The script [_1] ended in failure.",basename($chExec)).".\n");
return 0;
}
}
return 1;
}
inside $input there is the name of bat file whitch passed in argument, there is no $args in my case so the chExec variable is "C:\Users\anes.yahiaoui\Desktop\SPOOC_BD_TU_BD_XX_BD\tieme_RE_PCCA_BD_MAIN\RE_PCCA\BD\avantBDD\Gene_CSV\Proc\IMPORT_INV.bat".
when I call this function (Traitement_proc) my IMPORT_INV will start but I don't see which line do that ?
It's open executing the command. Both open(my $pipe, "shell_cmd |") and open(my $pipe, "-|", "shell_cmd") execute a shell command with the other end of the pipe in $pipe attached to its STDOUT.
For example,
use strict;
use warnings;
use feature qw( say );
use Win32::ShellQuote qw( quote_system_string );
open(my $pipe, quote_system_string(#ARGV)." |")
or die $!;
while (<$pipe>) {
chomp;
say("[$_]");
}
if (!close($pipe)) {
die("Error waiting for child to exit: $!\n") if $!;
die("Child killed by signal ".( $? & 0x7F )."\n") if $? & 0x7F;
die("Child exited with error ".( $? >> 8 )."\n") if $? >> 8;
}
say("Child completed successfully.");
>a.pl perl -le"print for 1..5"
[1]
[2]
[3]
[4]
[5]
Child completed successfully.

how to specify arguments to unix command in perl

I am trying to find the processes which are not running through perl. It works for some processes using following code but not for cgred service.
foreach $critproc (#critarray)
{
#system("/usr/bin/pgrep $critproc");
$var1=`/usr/bin/pgrep $critproc`;
print "$var1";
print "exit status: $?\n:$critproc\n";
if ($? != 0)
{
$probs="$probs $critproc,";
$proccrit=1;
}
}
For cgred I have to specify /usr/bin/pgrep -f cgred to check whether any pid is associated with it or not.
But when I specify -f in above code it gives exit status 0 ($?) to all the processes even if its not running.
Can you anyone tell me how to pass arguments to unix command in Perl.
Thanks
What's $critproc? Where's the -f you say is giving you problems? One might imagine you have some kind of escaping problem, but that shouldn't be the case if $critproc is cgred as you seem to imply.
Given these problem, I'm just going to answer the general question.
The following avoids the shell, so no need to build a shell command:
system("/usr/bin/pgrep", "-f", $critproc);
die "Killed by signal ".( $? & 0x7F ) if $? & 0x7F;
die "Exited with error ".( $? >> 8 ) if ($? >> 8) > 1;
my $found = !($? >> 8);
If you need a shell command, you can use String::ShellQuote's shell_quote to build it.
use String::ShellQuote qw( shell_quote );
my $shell_cmd = shell_quote("/usr/bin/pgrep", "-f", $critproc) . " >/dev/null";
system($shell_cmd);
die "Killed by signal ".( $? & 0x7F ) if $? & 0x7F;
die "Exited with error ".( $? >> 8 ) if ($? >> 8) > 1;
my $found = !($? >> 8);
or
use String::ShellQuote qw( shell_quote );
my $shell_cmd = shell_quote("/usr/bin/pgrep", "-f", $critproc);
my $pid = `$shell_cmd`;
die "Killed by signal ".( $? & 0x7F ) if $? & 0x7F;
die "Exited with error ".( $? >> 8 ) if ($? >> 8) > 1;
my $found = !($? >> 8);

Safe system call with multiple commands with perl

I have a Perl script that reads some information from a web form. In order to do proper sanitation, I want to use the system syntax described here.
They suggest that you should form system commands in the following form system ("cat", "/usr/stats/$username"); so that the username variable would only get interpreted as a argument to cat.
If I had a command that had the form of system("export REPLYTO=\"$from\"; echo \"$body\" | mail -s \"$subject\""); which has multiple system commands, how can I properly sanitize the system call?
Before I start, note that you can do the export in Perl by setting $ENV{REPLY_TO}.
Option 1.
You can use String::ShellQuote's shell_quote.
use autodie qw( :all );
my $cmd = shell_quote('echo', $body) .
'|' . shell_quote('mail', '-s', $subject);
local $ENV{REPLY_TO} = $from;
system($cmd);
Option 2.
Pass everything by env var.
use autodie qw( :all );
local $ENV{REPLY_TO} = $from;
local $ENV{SUBJECT} = $subject;
local $ENV{BODY} = $body;
system('echo "$BODY" | mail -s "$SUBJECT"');
Option 3.
Get rid of echo
use autodie qw( :all );
local $ENV{REPLY_TO} = $from;
open(my $pipe, '|-', 'mail', '-s', $subject);
print($pipe $body);
close($pipe);
die "Child died from signal ".($? & 0x7F)."\n" if $? & 0x7F;
die "Child exited from error ".($? >> 8)."\n" if $? >> 8;

How can I tell if a filehandle is empty in Perl?

For example:
open (PS , " tail -n 1 $file | grep win " );
I want to find whether the file handle is empty or not.
You can also use eof to check whether a file handle is exhausted. Here is an illustration based loosely on your code. Also note the use of a lexical file handle with the 3-arg form of open.
use strict;
use warnings;
my ($file_name, $find, $n) = #ARGV;
open my $fh, '-|', "tail -n $n $file_name | grep $find" or die $!;
if (eof $fh){
print "No lines\n";
}
else {
print <$fh>;
}
Although calling eof before you attempt to read from it produces the result you expect in this particular case, give heed to the advice at the end of the perlfunc documentation on eof:
Practical hint: you almost never need to use eof in Perl, because the input operators typically return undef when they run out of data, or if there was an error.
Your command will produce at most one line, so stick it in a scalar, e.g.,
chomp(my $gotwin = `tail -n 1 $file | grep win`);
Note that the exit status of grep tells you whether your pattern matched:
2.3 Exit Status
Normally, the exit status is 0 if selected lines are found and 1 otherwise …
Also, tail exits 0 on success or non-zero on failure. Use that information to your advantage:
#! /usr/bin/perl
use strict;
use warnings;
my $file = "input.dat";
chomp(my $gotwin = `tail -n 1 $file | grep win`);
my $status = $? >> 8;
if ($status == 1) {
print "$0: no match [$gotwin]\n";
}
elsif ($status == 0) {
print "$0: hit! [$gotwin]\n";
}
else {
die "$0: command pipeline exited $status";
}
For example:
$ > input.dat
$ ./prog.pl
./prog.pl: no match []
$ echo win >input.dat
$ ./prog.pl
./prog.pl: hit! [win]
$ rm input.dat
$ ./prog.pl
tail: cannot open `input.dat' for reading: No such file or directory
./prog.pl: no match []
open (PS,"tail -n 1 $file|");
if($l=<PS>)
{print"$l"}
else
{print"$file is empty\n"}
well ... scratch this ... I didn't make the connection about the filehandle actually being the output of a pipe.
You should use stat to determine the size of a file but you're going to need to
ensure the file is flushed first:
#!/usr/bin/perl
my $fh;
open $fh, ">", "foo.txt" or die "cannot open foo.txt - $!\n";
my $size = (stat $fh)[7];
print "size of file is $size\n";
print $fh "Foo";
$size = (stat $fh)[7];
print "size of file is $size\n";
$fh->flush;
$size = (stat $fh)[7];
print "size of file is $size\n";
close $fh;

Why is IPC::Open2::open2 returning the parent process ID?

I have the following script running in Perl 5.10 in cygwin:
use IPC::Open2;
use Symbol qw(gensym);
my $in = gensym();
my $out = gensym();
my $pid = open2($out, $in, "$exe");
waitpid $pid, 0;
The value of $pid is the PID of the perl process running, not that of the executable pointed to by $exe. Any ideas?
I just ran:
#!/usr/bin/perl
use strict;
use warnings;
use IPC::Open2;
my ($in, $out);
my $pid = open2($out, $in, ls => qw(-R /));
warn $pid, "\n";
waitpid $pid, 0;
__END__
and observed:
2916 2620 2916 2912 con 1003 14:49:56 /usr/bin/perl
O 2088 2916 2916 4064 con 1003 14:49:57 /usr/bin/ls
Why are you using the gensym stuff anyway?
This seems to work for me with Strawberry Perl 5.10 and cygwin. I output both process IDs to ensure I'm looking at the right things. I also put something in $exe so there's a command to execute. Curiously, open2 works even when $exe is undef and still returns a PID that isn't the parent process ID.
use IPC::Open2;
use Symbol qw(gensym);
$exe = 'cmd.exe /c dir /b';
my $in = gensym();
my $out = gensym();
my $pid = open2($out, $in, $exe);
print "I am pid $$: open2 is pid $pid\n";
close $in;
print <$out>;
waitpid $pid, 0;
You don't need the gensym stuff. open2 will autogenerate the filehandles if its arguments are lvalues that are undef.