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.
Related
This excellent question
How to read to and write from a pipe in Perl?
provides an excellent answer.
It doesn't work on ActiveState Perl.
The BUGS section of perlfork
http://docs.activestate.com/activeperl/5.14/lib/pods/perlfork.html
says
In certain cases, the OS-level handles created by the pipe(), socket(), and accept() operators
are apparently not duplicated accurately in pseudo-processes. This only happens in some situations,
but where it does happen, it may result in deadlocks between the read and write ends of pipe
handles, or inability to send or receive data across socket handles.
It is not clear what 'not duplicated accurately' means or if it even applies in this case.
Here is the test program
#! /usr/bin/env perl
use strict;
use warnings;
my $isActiveStatePerl = 1 ; # defined(&Win32::BuildNumber);
sub pipeFromFork
{
return open($_[0], "-|") if (!$isActiveStatePerl);
pipe $_[0], my $child or die "cannot create pipe";
printf STDERR "$$: pipe create parent %d child %d\n", fileno($_[0]), fileno($child);
my $pid = fork();
die "fork failed: $!" unless defined $pid;
if ($pid) { # parent
printf STDERR "$$: fork parent close child %d\n", fileno($child);
close $child;
} else { # child
open(STDOUT, ">&=", $child) or die "cannot clone child to STDOUT";
printf STDERR "$$: fork child close parent %d stdout %d\n", fileno($_[0]), fileno(STDOUT);
close $_[0];
}
return $pid;
}
my #transform = qw( tr [A-Za-z] [N-ZA-Mn-za-m] ); # rot13
my #inception = (
"V xabj, Qnq. Lbh jrer qvfnccbvagrq gung V pbhyqa'g or lbh.",
"V jnf qvfnccbvagrq gung lbh gevrq.",
);
sub snow_fortress { print STDERR "$$: 1 start\n"; print map "$_\n", #inception }
sub hotel
{
printf STDERR "$$: 2 start %d\n", fileno(STDIN);
# my $pid = open STDIN, "-|";
my $fh;
my $pid = pipeFromFork($fh);
print STDERR "$$: hotel: pid $pid\n";
defined($pid) or die "$0: fork: $!";
if (0 == $pid) {
snow_fortress;
print STDERR "$$: 1 exit\n";
exit(0);
}
open(STDIN, "<&", $fh) or die "cannot clone to STDIN";
printf STDERR "$$: 2 exec %d\n", fileno(STDIN);
# print while <STDIN>;
exec #transform or die "$0: exec: $!";
}
# my $pid = open my $fh, "-|";
my $pid = pipeFromFork(my $fh);
defined($pid) or die "$0: fork: $!";
print STDERR "$$: outer: pid $pid\n";
if (0 == $pid) {
hotel;
print STDERR "$$: 2 exit\n";
exit(0);
}
print STDERR "$$: 3 start " . fileno($fh) . "x\n";
print while <$fh>;
print STDERR "$$: 3 end\n";
close $fh or warn "$0: close: $!";
Option 1
-- if your input perl process is simple enough to put into a one liner
my $cmd = "perl -e ' -- your simple perl -- ' | cmdToExecute";
my $out;
open my $cmdpipe "-|", $cmd;
while (<$cmdpipe>) {
$out .= $_;
}
# $out is your captured output
-- if your input perl process is complicated, put it into a file
my $cmd = "perl compInput.pl | cmdToExecute";
# rest as above
Option 2
- remove ActiveState perl
- install git for windows and use the perl from it.
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;
This question already has answers here:
How do I get the output of an external command in Perl?
(7 answers)
Closed 8 years ago.
I am writing a Perl script to automate some software installation.
In my script I run another bash script and take its output and print it again.
print `/home/me/build.sh`;
but build.sh script take 8 minutes, so my script wait till the 8 minutes and script finishes the starting in printing the output.
How can I print each line from the build.sh program as it is running in bash shell?
As the comment below I use system ("/home/me/build.sh");
but the output goes to shell however I make out redirection in my script to my log file,
open $fh, "> filename";
*STDOUT = $fh;
*STDERR = $fh;
Then should when I use system function its output will be redirected to filename, but it isn't.
Should I use print system ("/home/me/build.sh"); instead of system ("/home/me/build.sh");?
#
The full code:
#!/usr/bin/perl
use strict;
use warnings;
use IO::File;
my %DELIVERIES = ();
my $APP_PATH = $ENV{HOME};
my $LOG_DIR = "$APP_PATH/logs";
my ($PRG_NAME) = $0 =~ /^[\/.].*\/([a-zA-Z]*.*)/;
main(#argv);
sub main
{
my #comps = components_name();
my $comp;
my $pid;
while ( scalar #comps ) {
$comp = pop #comps;
if ( ! ($pid = fork) ) {
my $filename = lc "$LOG_DIR/$comp.log";
print "$comp delpoyment started, see $filename\n";
open (my $logFile, ">", "$filename") or (die "$PRG_NAME: $!" && exit);
*STDOUT = $logFile;
*STDERR = $logFile;
deploy_component ( $comp );
exit 0;
}
}
my $res = waitpid (-1, 0);
}
sub components_name
{
my $FILENAME="$ENV{HOME}/components";
my #comps = ();
my $fh = IO::File->new($FILENAME, "r");
while (<$fh>)
{
push (#comps, $1) if /._(.*?)_.*/;
chomp ($DELIVERIES{$1} = $_);
}
return #comps;
}
sub deploy_component
{
my $comp_name = shift;
print "\t[umask]: Changing umask to 007\n";
`umask 007`;
print "\t[Deploing]: Start the build.sh command\n\n";
open (PIPE, "-|", "/build.sh");
print while(<PIPE>);
}
A more flexible way is to use pipe.
open PIPE, "/home/me/build.sh |";
open FILE, ">filename";
while (<PIPE>) {
print $_; # print to standard output
print FILE $_; # print to filename
}
close PIPE;
close FILE;
BTW, print system ("/home/me/build.sh"); will print the return value of system(), which is the exit status of your shell script, not the output wanted.
How can I print each line from the build.sh program as it is running in bash shell?
Possible Solution:
You can try the following
system ("sh /home/me/build.sh | tee fileName");
The above statement will show the output of build.sh on the console and at the same time write that output in the filename provided as the argument for tee
I tried putting a script i saw together, plus used an existing script to make something run as a service. Now I have the following pl script and the init.d / start/stop scripts.
They work, but I am wondering if I did it right, because when I start the service and i would start it again, it would just start again and give a new PID number (is this what I want? shouldn't it be saying "already running?")
Also what I didn't understand is what the "cache" part of the STDIN and STDOUT does.
Nor the filecheck (file set in the beginning and in the final loop checked for newer version...not sure what that does)
Here goes:
#!/usr/bin/perl
#use strict;
use POSIX;
use DateTime;
use Fcntl qw(:flock);
use File::CacheDir qw(cache_dir);
Log("Initializing...");
# Find and read config file
if (#ARGV != 1) {
print("Usage: miniserv.pl <config file>");
die;
}
if ($ARGV[0] =~ /^([a-z]:)?\//i) {
$config_file = $ARGV[0];
}
else {
print("NO CORRECT CONFIG FILE SPECIFIED");
die;
}
%config = &read_config_file($config_file);
Log("Initialized...");
Log("Loaded config file.");
my $file = $0;
my $age = -M $file;
Log("File - ".$file.", age - ".$age);
# Change dir to the server root
#roots = ( $config{'root'} );
for($i=0; defined($config{"extraroot_$i"}); $i++) {
push(#roots, $config{"extraroot_$i"});
}
chdir($roots[0]);
Log("Changed working directory: ".$roots[0]);
Status("Daemonizing...");
my $pid = fork;
if(!defined $pid)
{
LogError("Unable to fork : $!");
die;
}
if($pid)
{
Log("Parent process exiting, let the deamon (".$pid.") go...");
sleep 3;
exit;
}
POSIX::setsid;
if(-e $config{'pidfile'})
{
open(PID, "<".$config{'pidfile'});
my $runningpid = <PID>;
close PID;
unlink $config{'pidfile'};
while(-e "/proc/".$runningpid)
{
Status("Waiting for ".$runningpid." to exit...");
Log("Waiting for ".$runningpid." to exit...");
sleep 1;
}
}
open(PID, ">".$config{'pidfile'}) || die "Failed to create PID file $_[0] : $!";
print PID $$;
close PID;
Log("The deamon is now running...");
Status("Deamon running");
my $stdout = cache_dir({base_dir => $config{'root'}.'/cache', ttl => '1 day', filename => "STDOUT".$$});
my $stderr = cache_dir({base_dir => $config{'root'}.'/cache', ttl => '1 day', filename => "STDERR".$$});
Log("STDOUT : ".$stdout);
Log("STDERR : ".$stderr);
open STDIN, '/dev/null';
open STDOUT, '>>'.$stdout;
open STDERR, '>>'.$stderr;
while(1)
{
#### Code to be performed by the daemon
if($age - (-M $file))
{
Log("File modified, restarting");
open(FILE, $file ." |");
close(FILE);
last;
}
if(!-e $config{'pidfile'})
{
Log("Pid file doesn't exist, time go exit.");
last;
}
sleep 5;
}
sub Log
{
my $string = shift;
if($string)
{
my $time = DateTime->now();
if(open(LOG, ">>".$config{'logfile'}))
{
flock(LOG, LOCK_EX);
print LOG $$." [".$time->ymd." ".$time->hms."] - ".$string."\r\n";
close LOG;
}
}
}
sub LogError
{
my $string = shift;
if($string)
{
my $time = DateTime->now();
if(open(LOG, ">>".$config{'errorlog'}))
{
flock(LOG, LOCK_EX);
print LOG $$." [".$time->ymd." ".$time->hms."] - ".$string."\r\n";
close LOG;
}
}
}
sub Status
{
my $string = shift;
if($string)
{
$0 = "My Daemon- ".$string;
}
return $0;
}
# read_config_file(file)
# Reads the given config file, and returns a hash of values
sub read_config_file
{
local %rv;
if(-e $_[0])
{
open(CONF, $_[0]) || die "Failed to open config file $_[0] : $!";
while(<CONF>) {
s/\r|\n//g;
if (/^#/ || !/\S/) { next; }
/^([^=]+)=(.*)$/;
$name = $1; $val = $2;
$name =~ s/^\s+//g; $name =~ s/\s+$//g;
$val =~ s/^\s+//g; $val =~ s/\s+$//g;
$rv{$name} = $val;
}
close(CONF);
return %rv;
} else {
print("COULD NOT FIND CONFIG FILE");
die;
}
}
the start script
#!/bin/sh
echo Starting reliand server in /usr/libexec/reliand
trap '' 1
LANG=
export LANG
#PERLIO=:raw
unset PERLIO
export PERLIO
PERLLIB=/usr/libexec/reliand
export PERLLIB
exec '/usr/libexec/reliand/miniserv.pl' /etc/reliand/miniserv.conf
the init.d script
#!/bin/sh
# chkconfig: 235 99 10
# description: Start or stop the reliand server
#
### BEGIN INIT INFO
# Provides: reliand
# Required-Start: $network $syslog
# Required-Stop: $network
# Default-Start: 2 3 5
# Default-Stop: 0 1 6
# Description: Start or stop the reliand server
### END INIT INFO
start=/etc/reliand/start
stop=/etc/reliand/stop
lockfile=/var/lock/subsys/reliand
confFile=/etc/reliand/miniserv.conf
pidFile=/var/reliand/miniserv.pid
name='reliand'
case "$1" in
'start')
$start >/dev/null 2>&1 </dev/null
RETVAL=$?
if [ "$RETVAL" = "0" ]; then
touch $lockfile >/dev/null 2>&1
fi
;;
'stop')
$stop
RETVAL=$?
if [ "$RETVAL" = "0" ]; then
rm -f $lockfile
fi
pidfile=`grep "^pidfile=" $confFile | sed -e 's/pidfile=//g'`
if [ "$pidfile" = "" ]; then
pidfile=$pidFile
fi
rm -f $pidfile
;;
'status')
pidfile=`grep "^pidfile=" $confFile | sed -e 's/pidfile=//g'`
if [ "$pidfile" = "" ]; then
pidfile=$pidFile
fi
if [ -s $pidfile ]; then
pid=`cat $pidfile`
kill -0 $pid >/dev/null 2>&1
if [ "$?" = "0" ]; then
echo "$name (pid $pid) is running"
RETVAL=0
else
echo "$name is stopped"
RETVAL=1
fi
else
echo "$name is stopped"
RETVAL=1
fi
;;
'restart')
$stop ; $start
RETVAL=$?
;;
*)
echo "Usage: $0 { start | stop | restart }"
RETVAL=1
;;
esac
exit $RETVAL
The script will give you a new PID.
As for the cache file, it is storing the standard I/O streams; this is normal for Bash so the script does not keep having to perpetually create new streams each time it initializes the daemon.
I have this below code:
$cmd = system ("p4 change -o 3456789");
I want to print the output -description of the change list - into a file.
$cmd = system ("p4 change -o 3456789 > output_cl.txt");
will write the output in to output_cl.txt file.
But, is there anyway to get the output through $cmd?
open(OUTPUT, ">$output_cl.txt") || die "Wrong Filename";
print OUTPUT ("$cmd");
will write 0 or 1 to the file. How to get the output from $cmd?
To store the output of your p4 command into an array, use qx:
my #lines = qx(p4 change -o 3456789);
In addition to grabbing the entire output of a command with qx// or backticks, you can get a handle on a command's output. For example
open my $p4, "-|", "p4 change -o 3456789"
or die "$0: open p4: $!";
Now you can read $p4 a line at a time and possibly manipulate it as in
while (<$p4>) {
print OUTPUT lc($_); # no shouting please!
}
If you find it confusing remembering what you need to run in order to get a command's return value, vs. its output, or how to handle different return codes, or forget to right-shift the resulting code, you need IPC::System::Simple, which makes all this, well, simple:
use IPC::System::Simple qw(system systemx capture capturex);
my $change_num = 3456789;
my $output = capture(qw(p4 change -o), $change_num);
You can always use the following process to dump output straight to a file.
1) dup the system STDOUT file descriptor, 2) open STDOUT, 3) system, 4) copy the IO slot back into STDOUT:
open( my $save_stdout, '>&1' ); # dup the file
open( STDOUT, '>', '/path/to/output/glop' ); # open STDOUT
system( qw<cmd.exe /C dir> ); # system (on windows)
*::STDOUT = $save_stdout; # overwrite STDOUT{IO}
print "Back to STDOUT!"; # this should show up in output
But qx// is probably what you're looking for.
reference: perlopentut
Of course this could be generalized:
sub command_to_file {
my $arg = shift;
my ( $command, $rdir, $file ) = $arg =~ /(.*?)\s*(>{1,2})\s*([^>]+)$/;
unless ( $command ) {
$command = $arg;
$arg = shift;
( $rdir, $file ) = $arg =~ /\s*(>{1,2})\s*([^>]*)$/;
if ( !$rdir ) {
( $rdir, $file ) = ( '>', $arg );
}
elsif ( !$file ) {
$file = shift;
}
}
open( my $save_stdout, '>&1' );
open( STDOUT, $rdir, $file );
print $command, "\n\n";
system( split /\s+/, $command );
*::STDOUT = $save_stdout;
return;
}