How to do 'Wait and Answer to System' with Perl - perl

I am writing a Perl that automatically interacts with another script.
The script needs double confirm for some critical operations.
Executing the script without Perl is something like the following:
$ ./TheScript
TheScript Starting.......
Following step might be harmful to your system.
Are You Sure (Y/N)?
$ Y
TheScript finished!
Now I want a Perl script doing that for me.
I am sure that (Y/N) confirmation will exist within 10 sec. So I've tried:
system('./TheScript');
sleep 10;
system('Y');
This failed because it stuck in system('./TheScript') and did not
go to the rest of the script including reply 'Y'.
Backstick ` is almost the same as system except it captures the STDOUT.
exec() is more impossible because it forks TheScript and is not able to do anything on it again.
Did I make any mistakes doing the analysis? Or are there any functions doing what I want?

You misunderstand the system function. It waits for the program to exit before your Perl program continues.
To drive an interactive program from Perl, you want the Expect module (or perhaps Expect::Simple). However, for a very simple case like you're suggesting, IPC::Open2 may suffice, and it's a core module.

As per your written Perl script, you are facing issue when double confirmation occurred by the system. So for that I can suggest, you can write the script in such way that ,
first it checks first confirmation OK fine
if next again it asks confirmation , your script must check second confirmation as well
for this ,
my $conf= "Are You Sure (Y/N)?"; my $length = $conf; if ($length > 0) { sleep 0; system('Y'); } else { system('N'); }
I hope , this script will be fine for you.

Related

Perl run function on script exit/die

I have a question and could not find the answer i need. I have a perl script that works with several files. At the beginning of the script a given file is renamed and backed up, at the end of the script it is renamed back, so the original file is not touched at all.
But what if the script dies while running, e.g. if a file is missing or if the user exits the script via "cmd + c" on the keyboard? Is there a method to define a function that is always executed when the script dies or the user wants the script to die? I found the "END"-block from perl, but it don't think that will work in my case.
Thank you!
-Alex
The END block works for exit and die. If doesn't work for signals, though, you'll have to create a signal handler. Setting it to an empty sub would pass the control to the END block:
local $SIG{INT} = sub {};

Start perl script with Win32::Process::Create

I would like to translate all of the system commands in my script to Win::32::Process::Create commands. CPAN tells me the syntax:
Win32::Process::Create($obj,$appname,$cmdline,$iflags,$cflags,$curdir)
So, I tried to apply it:
Win32::Process::Create( $Win32processObj,
"C:\\Perl64\\bin\\perl.exe",
"'C:\\Users\\script.pl','$arg'",
0,
NORMAL_PRIORITY_CLASS,
"." ) || die "Failed to create process.\n";
When I run this, I don't get an error, but I don't start a new process either...
When I use GetProcessID(), I get a pid, but it doesn't correspond to anything in the tasklist... (I'm assuming the created process ends before I can see it displayed in the tasklist).
According to your comment, Windows says it created the process. Accoding to your question, you even have its process id of the process you claim never got created. I'm gonna go out on a limb and say you are mistaken.
You should now check what code the process ended with. perl -E'say $!=THECODE;' might give you a hint. But chances are it's because you tell Perl to execute a file named 'C:\Users\script.pl' (as opposed to C:\Users\script.pl).

Pause and Resume Perl Script based on Condition

I have written a Perl Script which connects to a set of URLs one by one. Each time it connects to a URL, it fetches the result and performs some operations on it.
However, there are a few possibilities where an error might occur.
The most important of these which I am trying to address is, availability issue. The Internet connection might stop working in between. So, lets say I have a list of 1000 URLs, the script has gone through 500 of them successfully and then Internet stops working, as a result, the script aborts with an error. I want to control this condition and implement a pause and resume feature here.
A few lines of code to explain:
$mech=WWW::Mechanize->new();
$mech->timeout(10);
$url="http://example.com/index.php?id=";
while(<INPUT>)
{
chomp $_;
$word=$_;
$url=$url.$word;
eval{$mech->get($url);};
if($#)
{
# Pause the Script once the if condition evaluates to true
}
....
}
There could be several conditions where I would want to pause the script.
So, I understand that I need to pass some kind of signal to the script which would cause it to pause. At the same time, there should be a functionality provided to the end user, who can press "some key combination" to resume the script.
My guess is that, this would involve some OS API Calls.
On Linux, there is a way to suspend a process, by pressing Ctrl+Z key combination.
However, I am not sure how this can be automated in the Perl Script.
Please let me know if you need more details in order to find a solution to this.
Thanks.
It's pretty standard to have a process blocked while waiting for a read. While it's blocked, it won't receive any CPU time. No OS calls, outside of what Perl normally does, would be needed. So just issue your prompt "Press enter when ready to resume, and read STDIN.
Blocked-on-IO is standard as a state for an OS's process tables. Quite recently, I wrote a socket test script in Perl to work as a dummy server. I left in a haste one night and found "Waiting on port 3371..." prompt on my dummy server's terminal in the morning. It would have stayed like that for weeks.
So, you can wrap the whole thing in a labeled IO-loop, and instead of die-ing or croaking use loop controls like so:
BIT_OF_WORK:
while ( $more_work ) {
ALL_GOOD:
while ( $all_good ) {
...
my $socket = open_socket() or last ALL_GOOD;
...
last BIT_OF_WORK unless ( $more_work = #queue );
...
}
say 'Detected Network Down. Press Enter to continue';
my $user_input = <STDIN>;
}
say 'Phew, that was a lot of work!';
You might find redo more to your tastes even.

Killing an application started using system() in Perl

I am trying to run an application inside a Perl script using system(). The application I'm running gets stuck sometimes (it enters some kind of infinite loop). Is there a way I can know if this application is stuck and kill it to continue with the Perl script?
I'm trying to do something like this:
start testapp.exe;
if(stuck with testapp.exe) {
kill testapp.exe;
}
Determining if "it is stuck in infinite loop" is called Halting Problem and is undecidable.
If you want to kill it, you will have to fork the application using fork and then kill it from the other fork, if it is going for too long.
You can determine if the proccess is going for too long by this
use POSIX ":sys_wait_h";
waitpid($pid, WNOHANG)>0 #waitpid returns 0 if it still running
at least, according to this manual page
I am not sure how well it works on various systems, you can try it out.
Not a direct answer, but I can recommend using forks module if you want to fork with ease, but it works only on UNIX systems (not windows).
OK, more helping code :) It works in UNIX, according to perlfork perldoc, it should work on Windows exactly the same way.
use warnings;
use strict;
use POSIX ":sys_wait_h";
my $exited_cleanly; #to this variable I will save the info about exiting
my $pid = fork;
if (!$pid) {
system("anything_long.exe"); #your long program
} else {
sleep 10; #wait 10 seconds (can be longer)
my $result = waitpid(-1, WNOHANG); #here will be the result
if ($result==0) { #system is still running
$exited_cleanly = 0; #I already know I had to kill it
kill('TERM', $pid); #kill it with TERM ("cleaner") first
sleep(1); #wait a bit if it ends
my $result_term = waitpid(-1, WNOHANG);
#did it end?
if ($result_term == 0) { #if it still didnt...
kill('KILL', $pid); #kill it with full force!
}
} else {
$exited_cleanly = 1; #it exited cleanly
}
}
#you can now say something to the user, for example
if (!$exited_cleanly) {...}
system("start testapp")
is short for
system("cmd", "/c", "start testapp")
Perl just knows about cmd; it doesn't know anything about start, much less about testapp. system is not the tool you want. That's the first problem.
The second problem is that you haven't defined what it means to be "stuck". If you want to monitor a program, it needs a heartbeat. A heartbeat is a periodic activity that can be externally examined. It can be writing to a pipe. It can be changing a file. Anything.
The monitoring program listens for this heartbeat, and presumes the program is dead if the heart stops beating, so to speak.
"Killing" is done using signals in unix, but it's done using TerminateProcess in Windows. The third problem is that Perl core does not give you access to that function.
The solution to the first and third problem is Win32::Process. It allows you to launch a process in the background, and it also allows you to terminate it.
Creating a heartbeat is up to you.
Here is one way you can handle the problem if you know that testapp should not take more than N seconds to do its thing, then you can use a timeout to kill the app by way of IPC::Run.
In the example below there is a timeout of 1 second which kills the sleep 10 command that takes too long (longer than the timeout of 1 second). If this doesn't do what you want, then you should provide more information about how you can detect that testapp.exe is "stuck".
#!/usr/bin/env perl
use IPC::Run qw( run timeout );
eval { # if (stuck with testapp.exe for more than N seconds)
#cmd = ('sleep', '10'); # this could be testapp.exe instead of sleep
run \#cmd, \$in, \$out, \$err, timeout( 1 ) or die "test"; # start testapp.exe
print "do stuff if cmd succeeds\n";
};
print "more stuff to do afterwards whether or not command fails or succeeds\n";
You can't determine that the application is stuck if you execute it like that, because the system statement won't return until the application terminates.
So, at least, you need to start the test application so it can run asynchronously from the Perl script that is to monitor it.
Having resolved that part of the problem, you have to establish a mechanism that will allow the monitoring Perl script to determine that the application is stuck. That is a non-trivial exercise, and likely system dependent, unless you adopt a simple expedient such as requiring the application to write a heart-beat indication somewhere, and the Perl script monitors for the heart-beat. For example (not necessarily a good example), the application could write the current time into a file identified by its PID, and the Perl script could monitor the file to see if the heart-beat is sufficiently recent. Of course, this assumes that the 'infinite loop' doesn't include code that writes to the heart-beat file.

How to run a script and not wait for it in Perl?

I have a system call in Perl that looks like this:
system('/path/to/utility >> /redirect/to/log file');
But this waits for utility to complete. I just want to trigger this and let the Perl script finish irrespective of whether the utility call finished or not.
How can I do this?
I tried changing the line to
system('/path/to/utility >> /redirect/to/log file &');
but this syntax still waits for the call to finish on Windows. I need to make it work on Linux as well as Windows.
if ($^O eq 'MSWin32') {
system('start "" \\path\\to\\utility >> \\redirect\\to\\log_file');
} else {
system('/path/to/utility >> /redirect/to/log_file &');
}
or
if ($^O eq 'MSWin32') {
system(1, '\\path\\to\\utility >> \\redirect\\to\\log_file');
} else {
system('/path/to/utility >> /redirect/to/log_file &');
}
You could try looking at the fork keyword, and launch your system command from the forked process. See the perlipc manpage for examples.
This common task has been abstracted into the CPAN module Proc::Background
The easiest way on POSIX-like systems is in fact the one you already tried:
system('/path/to/utility >> /redirect/to/log_file &');
The easiest way to get a tool to execute in background on Windows is to use start.exe as a launch helper:
system('start /path/to/utility >> /redirect/to/log_file');
However, I don't know if it works to redirect the output to log file that way and I have no Windows system around this very moment for testing it.
Yes, that means you would need a code branch depending on current system, but other solutions may need one, too. If your Perl has fork() emulation, you can in fact use fork() on both systems (this is a bit more complicated, since you cannot redirect stdout to a logfile that easy, you first have to open it in Perl and make it stdout of the forked child before calling the new process). If your Windows Perl has no fork() emulation, you also need a code branch, since in that case you can use fork() only on UNIX and you'll need to use Win32::Process::Create with the DETACHED_PROCESS option on Windows.
But maybe you can first let us know if using start is already working for you. If it does not, maybe start.exe doesn't handle slashes. In that case you may have to use something like
C:\\path\\to\\utility
instead (double backslash is important! A single backslash has a special meaning in string literals; it's the escape character).
To find out if you are running on Windows or not, take a look at the variable $OSNAME or $^OS, it should say something like "MSWin32" on Windows.