Perl run function on script exit/die - perl

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 {};

Related

Can I pass #ARGV and options to a perl script invoked via do?

I have a script which processes #ARGV and options (via Getopt::Long::Descriptive).
Now, I would also like to call that same script from another perl program, and pass variables to it.
One solution is to use system, and build the arguments passed accordingly like so:
system("perl my_script.pl", qw/--foo bar --baz 2/);
My question is: can I obtain the same result by calling the script via do?
I'm trying to do this because the script would run inside a Minion job queue so I would avoid spawning off a perl instance every time - that often causes out-of-memory issues.
rajashekar's answer is correct, but I would also add you can set args for the child script in a local block so that the child doesn't need to corrupt #ARGV in the parent.
{
local #ARGV = ("--foo","bar","--baz",$ARGV[3]);
do 'my_script.pl';
}
# previous #ARGV restored at end of block
You can set the #ARGV yourself and then evaluate the script file.
This will work
#ARGV = qw(--foo --bar).
do $script_file;
Since #ARGV is a global variable it is available inside do too. But if you want to access any local variables you have defined, then you can to use:
eval `cat $script_file`;
EDIT: I had mistakenly assumed that #ARGV will not be accessible within do.

How do I make a perl script run another perl script?

I am writing a large Perl script, which needs to utilize other existing Perl scripts. The problem is the main script needs to reference many different scripts from different folders. For example the main script would be contained in:
/perl/programs/io
It may need to run a script which is stored in:
/perl/programs/tools
Note that there are other orthogonal folders besides tools so I need to be able to access any of them on the fly.
Currently this is what I got:
my $mynumber = '../tools/convert.pl bin2dec 1011';
In theory it should move back from the io directory then enter the appropriate tool directory and call the convert.pl script while passing it the parameters.
All this does is store the string in the single quotes to $myNumber.
I like to assign the output of a command to an array so I can loop through the array to find error or other messages. For example if I'm making a zip file to email to someone I want to check to see if the zip program had any errors before I continue to make and send the email.
#msgs = `zip -f myfile.zip *.pl`; # Use backticks
You can also assign the output to a scalar:
$msg = `ls -al *.pl`; # Use backticks
To run any system command or script all you have to do is use `backticks`. From observing another programer's perl code, I misread these strange quotes for 'single quotes'.
backticks are also nice because they return the text in STDOUT to your perl script so that the output can be assigned to a variable, something I have found impossible if using system("");
The similar question answer does not work with my version of perl. The line
use IPC::System::Simple qw(system capture);
throws some errors. However just using system works, like this:
my $mynumber = system($^X, "../tools/convert.pl", 'bin2dec', '1011');
I can use the above without setting equal to something to execute scripts which return no value and are only sent arguments.
This seems to be the easiest way to do what I need to and the entire programs folder can be moved anywhere and it will still work as no parent directories above programs are used.

How to do 'Wait and Answer to System' with 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.

How can I determine if the script is being executed within a system or qx call in Perl?

In Perl, is it possible to determine if a script is being executed within another script (presumably via system or qx)?
$ cat foo.pl
print "foo";
print "\n" if not $in_qx; # or the like.
I realize this is not applicable if the script was being run via exec.
I know for certain that system runs the process as a fork and I know fork can return a value that is variable depending on whether you are in the parent or the child process. Not certain about qx.
Regardless, I'm not certain how to figure out if I'm in a forked process without actually performing a fork.
All processes are forked from another process (except init). You can sort of tell if the program was run from open, qx//, open2, or open3 by using the isatty function from POSIX, but there is no good way to determine if you are being run by system without looking at the process tree, and even then it can get murky (for instance system "nohup", "./foo.pl" will not have the calling perl process as its parent).
You could check "who's your daddy", using "getppid" (get parent id). Then check if your parent id is a perl script with pgrep or similar.
Do you control the caller? The simplest thing to do would be to pass an argument, e.g. --isforked.

How do I run a Perl script from within a Perl script?

I've got a Perl script that needs to execute another Perl script. This second script can be executed directly on the command line, but I need to execute it from within my first program. I'll need to pass it a few parameters that would normally be passed in when it's run standalone (the first script runs periodically, and executes the second script under a certain set of system conditions).
Preliminary Google searches suggest using backticks or a system() call. Are there any other ways to run it? (I'm guessing yes, since it's Perl we're talking about :P ) Which method is preferred if I need to capture output from the invoked program (and, if possible, pipe that output as it executes to stdout as though the second program were invoked directly)?
(Edit: oh, now SO suggests some related questions. This one is close, but not exactly the same as what I'm asking. The second program will likely take an hour or more to run (lots of I/O), so I'm not sure a one-off invocation is the right fit for this.)
You can just do it.
{
local #ARGV = qw<param1 param2 param3>;
do '/home/buddy/myscript.pl';
}
Prevents the overhead of loading in another copy of perl.
The location of your current perl interpreter can be found in the special variable $^X. This is important if perl is not in your path, or if you have multiple perl versions available but which to make sure you're using the same one across the board.
When executing external commands, including other Perl programs, determining if they actually ran can be quite difficult. Inspecting $? can leave lasting mental scars, so I prefer to use IPC::System::Simple (available from the CPAN):
use strict;
use warnings;
use IPC::System::Simple qw(system capture);
# Run a command, wait until it finishes, and make sure it works.
# Output from this program goes directly to STDOUT, and it can take input
# from your STDIN if required.
system($^X, "yourscript.pl", #ARGS);
# Run a command, wait until it finishes, and make sure it works.
# The output of this command is captured into $results.
my $results = capture($^X, "yourscript.pl", #ARGS);
In both of the above examples any arguments you wish to pass to your external program go into #ARGS. The shell is also avoided in both of the above examples, which gives you a small speed advantage, and avoids any unwanted interactions involving shell meta-characters. The above code also expects your second program to return a zero exit value to indicate success; if that's not the case, you can specify an additional first argument of allowable exit values:
# Both of these commands allow an exit value of 0, 1 or 2 to be considered
# a successful execution of the command.
system( [0,1,2], $^X, "yourscript.pl", #ARGS );
# OR
capture( [0,1,2, $^X, "yourscript.pl", #ARGS );
If you have a long-running process and you want to process its data while it's being generated, then you're probably going to need a piped open, or one of the more heavyweight IPC modules from the CPAN.
Having said all that, any time you need to be calling another Perl program from Perl, you may wish to consider if using a module would be a better choice. Starting another program carries quite a few overheads, both in terms of start-up costs, and I/O costs for moving data between processes. It also significantly increases the difficulty of error handling. If you can turn your external program into a module, you may find it simplifies your overall design.
All the best,
Paul
I can think of a few ways to do this. You already mentioned the first two, so I won't go into detail on them.
backticks: $retVal = `perl somePerlScript.pl`;
system() call
eval
The eval can be accomplished by slurping the other file into a string (or a list of strings), then 'eval'ing the strings. Heres a sample:
#!/usr/bin/perl
open PERLFILE, "<somePerlScript.pl";
undef $/; # this allows me to slurp the file, ignoring newlines
my $program = <PERLFILE>;
eval $program;
4 . do: do 'somePerlScript.pl'
You already got good answers to your question, but there's always the posibility to take a different point of view: maybe you should consider refactoring the script that you want to run from the first script. Turn the functionality into a module. Use the module from the first and from the second script.
If you need to asynchronously call your external script -you just want to launch it and not wait for it to finish-, then :
# On Unix systems, either of these will execute and just carry-on
# You can't collect output that way
`myscript.pl &`;
system ('myscript.pl &');
# On Windows systems the equivalent would be
`start myscript.pl`;
system ('start myscript.pl');
# If you just want to execute another script and terminate the current one
exec ('myscript.pl');
Use backticks if you need to capture the output of the command.
Use system if you do not need to capture the output of the command.
TMTOWTDI: so there are other ways too, but those are the two easiest and most likely.
See the perlipc documentation for several options for interprocess communication.
If your first script merely sets up the environment for the second script, you may be looking for exec.
#!/usr/bin/perl
use strict;
open(OUTPUT, "date|") or die "Failed to create process: $!\n";
while (<OUTPUT>)
{
print;
}
close(OUTPUT);
print "Process exited with value " . ($? >> 8) . "\n";
This will start the process date and pipe the output of the command to the OUTPUT filehandle which you can process a line at a time. When the command is finished you can close the output filehandle and retrieve the return value of the process. Replace date with whatever you want.
I wanted to do something like this to offload non-subroutines into an external file to make editing easier. I actually made this into a subroutine. The advantage of this way is that those "my" variables in the external file get declared in the main namespace. If you use 'do' they apparently don't migrate to the main namespace. Note the presentation below doesn't include error handling
sub getcode($) {
my #list;
my $filename = shift;
open (INFILE, "< $filename");
#list = <INFILE>;
close (INFILE);
return \#list;
}
# and to use it:
my $codelist = [];
$codelist = getcode('sourcefile.pl');
eval join ("", #$codelist);