I'm trying to grasp the concept of fork() & exec() for my own learning purposes. I'm trying to use perl fork as a second identical process, and then use that to exec a .sh script.
If I use fork() & exec() can I get the .sh script to run in parallel to my perl script? Perl script doesn't wait on the child process and continues on its execution. So my perl script doesn't care about the output of the child process, but only that the command is valid and running. Sort of like calling the script to run in the background.
Is there some sort of safety I can implement to know that the child process exited correctly as well?
If I use fork() & exec() can I get the .sh script to run in parallel to my perl script? [...] Sort of like calling the script to run in the background.
Yes. Fork & exec is actually the way shells run commands in the background.
Is there some sort of safety I can implement to know that the child process exited correctly as well?
Yes, using waitpid() and looking at the return value stored in $?
Like #rohanpm mentioned, the perlipc man page has a lot of useful examples showing how to do this. Here is one of the most relevant, where a signal handler is set up for SIGCHLD (which will be sent to the parent when the child terminates)
use POSIX ":sys_wait_h";
$SIG{CHLD} = sub {
while ((my $child = waitpid(-1, WNOHANG)) > 0) {
$Kid_Status{$child} = $?;
}
};
To get waitpid to not wait for the child:
use POSIX qw/ WNOHANG /;
my $st = waitpid $pid, WNOHANG;
$st is 0 if the process is still running and the pid if it's reaped.
Related
there. I'm trying to do the following:
Fork.
Launched a desktop application.
Wait for 2 secs.
Kill the desktop application.
This is what I have:
#!/usr/bin/perl
use utf8;
use strict;
use warnings;
my #cmd = ('calc.exe');
my $pid = fork();
die "fork() failed: $!" unless defined $pid;
if ($pid) {
system #cmd;
}
sleep 2;
kill 'TERM' => $pid;
The application is launched correctly but it doesn't kill after the two seconds. I know I missing something, I hope someone can point me to the right direction. Right know I'm testing this code in windows 7 SP1 with perl 5.32.1 bundled with msys2.
You have up to four processes here: Parent, child, shell, app. You're killing the child, not the app. Use exec instead of system and use the exec BLOCK LIST form to avoid a shell.
Even then, that may not work. It depends on how well msys2 emulates fork and exec. A better solution might be to use the Windows system calls (CreateProcess and ExitProcess) instead of emulations of unix system calls.
I am working on a capstone project and am hoping for some insight.
This is the first time I've worked with Perl and it's pretty much a basic Perl script to automate a few different Unix commands that need to be executed in a specific order. There are two lines throughout the script which executes a Unix command that needs to finish processing before it is acceptable for the rest of the script to run (data will be incorrect otherwise).
How am I able to use Perl (or maybe this is a Unix question?) to print a simple string once the Unix command has finished processing? I am looking into ways to read in the Unix command name but am not sure how to implement a way to check if the process is no longer running and to print a string such as "X command has finished processing" upon it's completion.
Example:
system("nohup scripts_pl/RunAll.pl &");
This runs a command in the background that takes time to process. I am asking how I can use Perl (or Unix?) to print a string once the process has finished.
I'm sorry if I didn't understand your asking context.
But couldn't you use perl process fork function instead of & if you would like to do parallel process?
# parent process
if (my $pid = fork) {
# this block behaves as a normal process
system("nohup scripts_pl/RunAll2.pl"); # you can call other system (like RunAll2.pl)
wait; # wait for the background processing
say 'finished both';
}
# child process
else {
# this block behaves as a background process
system("nohup scripts_pl/RunAll.pl"); # trim &
}
You could try to use IPC::Open3 instead of system:
use IPC::Open3;
my $pid = open3("<&STDIN", ">&STDOUT", ">&STDERR", 'nohup scripts_pl/RunAll.pl');
waitpid( $pid, 0 );
Or, if you need to run nohup through the shell:
my $pid = open3("<&STDIN", ">&STDOUT", ">&STDERR", 'bash','-c', 'nohup scripts_pl/RunAll.pl & wait');
Update: Thanks to #ikegami. A better approach if you would like STDIN to stay open after running the command:
open(local *CHILD_STDIN, "<&", '/dev/null') or die $!;
my $pid = open3("<&CHILD_STDIN", ">&STDOUT", ">&STDERR", 'nohup scripts_pl/RunAll.pl');
I have to run a Bash command. But this command will take a few minutes to run.
If I execute this command normally (synchronously), my application will hang until the command is finished running.
How do I run Bash commands asynchronously from a Perl script?
You can use threads to start Bash asynchronously,
use threads;
my $t = async {
return scalar `.. long running command ..`;
};
and later manually test if thread is ready to join, and get output in a non-blocking fashion,
my $output = $t->is_joinable() && $t->join();
If you do not care about the result, you can just use system("my_bash_script &");. It will return immediately and the script does what is needed to be done.
I have two files:
$ cat wait.sh
#!/usr/bin/bash
for i in {1..5}; { echo "wait#$i"; sleep 1;}
$cat wait.pl
#!/usr/bin/perl
use strict; use warnings;
my $t = time;
system("./wait.sh");
my $t1 = time;
print $t1 - $t, "\n";
system("./wait.sh &");
print time - $t1, "\n";
Output:
wait#1
wait#2
wait#3
wait#4
wait#5
5
0
wait#1
wait#2
wait#3
wait#4
wait#5
It can be seen that the second call returns immediately, but it keeps writing to the stdout.
If you need to communicate to the child then you need to use fork and redirect STDIN and STDOUT (and STDERR). Or you can use the IPC::Open2 or IPC::Open3 packages. Anyhow, it is always a good practice to wait for the child to exit before the caller exits.
If you want to wait for the executed processes you can try something like this in Bash:
#!/usr/bin/bash
cpid=()
for exe in script1 script2 script3; do
$exe&
cpid[$!]="$exe";
done
while [ ${#cpid[*]} -gt 0 ]; do
for i in ${!cpid[*]}; do
[ ! -d /proc/$i ] && echo UNSET $i && unset cpid[$i]
done
echo DO SOMETHING HERE; sleep 2
done
This script at first launches the script# asynchronously and stores the pids in an array called cpid. Then there is a loop; it browses that they are still running (/proc/ exists). If one does not exist, text UNSET <PID> is presented and the PID is deleted from the array.
It is not bulletproof as if DO SOMETHING HERE part runs very long, then the same PID can be added to another process. But it works well in the average environment.
But this risk also can be reduced:
#!/usr/bin/bash
# Enable job control and handle SIGCHLD
set -m
remove() {
for i in ${!cpid[*]}; do
[ ! -d /proc/$i ] && echo UNSET $i && unset cpid[$i] && break
done
}
trap "remove" SIGCHLD
#Start background processes
cpid=()
for exe in "script1 arg1" "script2 arg2" "script3 arg3" ; do
$exe&
cpid[$!]=$exe;
done
#Non-blocking wait for background processes to stop
while [ ${#cpid[*]} -gt 0 ]; do
echo DO SOMETHING; sleep 2
done
This version enables the script to receive the SIGCHLD signal when an asynchronous sub process exited. If SIGCHLD is received, it asynchronously looks for the first non-existent process. The waiting while-loop is reduced a lot.
The normal way to do this is with fork. You'll have your script fork, and the child would then call either exec or system on the Bash script (depending on whether the child needs to handle the return code of the Bash script, or otherwise interact with it).
Then your parent would probably want a combination of wait and/or a SIGCHILD handler.
The exact specifics of how to handle it depend a lot on your situation and exact needs.
I have a script which executes few commands and then telnets to machine. Now I need to call this script from another perl script.
$result = `some_script.pl`;
The script some_script.pl executes successfully but I am not able to exit from the main script as the script waits at the telnet prompt.
I also need to capture the exit status of the script in order to make sure that some_script.pl executed successfully.
I cannot modify some_script.pl.
Is there some way by which I can issue quit after the some_script.pl is executed successfully?
Try this out, this 'magic' close the standard in/out/err and may let your program finish.
$result = `some_script.pl >&- 2>&- <&-';
Otherwise you could use open2 and expect to watch for a specific string (like Done!) in your program output and close it when done.
http://search.cpan.org/~rgiersig/Expect-1.15/Expect.pod
Regards
I don't like the way you are actually executing your perl script with a "backtick" call to the system.
I suggest you actually fork (or something equivalent) and run the program in a more controlled manner.
use POSIX ":sys_wait_h";
my $pid = fork();
if($pid) { # on the parent proc, $pid will point to the child
waitpid($pid); # wait for the child to finish
} else { # this is the child, where we want to run the telnet
exec 'some_script.pl'; # this child will now "become" some_script.pl
}
Since I don't know how some_script.pl actually works, I cannot really help you more here. But for example, if all you need to do is print "quit" on the command line of some_script.pl you could use IPC::Open2 like suggested in another question. Doing something like:
use IPC::Open2;
$pid = open2(\*CHLD_OUT, \*CHLD_IN, 'some_script.pl');
print CHLD_IN "quit\n";
waitpid( $pid, 0 );
my $child_exit_status = $? >> 8;
You do need to tweak this a little, but the idea should solve your problem.
I wrote this code that should open several process , the problem is its work on linux well but when i execute it on windows its just create one process !!. is this possible to create multiprocess on windows with perl ?
$j = ARGV[0];
for($i=1; $i<=$j; $i++){
system("perl example.pl word.txt.$i &");
}
& is a *nix thing. An explicit fork in Windows will do it.
Bear in mind that Windows implementations of Perl emulate forking using threads, so that may be another option.
my #pids;
for my $i (1 .. $j) {
my $pid = fork;
unless ( $pid ) { # Child
system("perl example.pl word.txt.$i");
exit 0;
}
push #pids, $pid;
}
waitpid $_, 0 foreach #pids;
Better fork from the enclosing Perl script and then call system in the child process without the trailing &. wait will be needed in the parent as well.
Because the argument of system is parsed by the system shell, you will encounter different behaviour from the Windows shell than from Bash, for example.
It is a lot easier to use the START command (Windows Batch command) than to fork processes. The downside is that it will open multiple DOS windows.
system "start perl example.pl word.txt.$i";