Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl Monk, Perl Meditation
 
PerlMonks  

calling a perl script within a perl script

by sbp (Initiate)
on Nov 27, 2005 at 01:46 UTC ( [id://511969]=perlquestion: print w/replies, xml ) Need Help??

sbp has asked for the wisdom of the Perl Monks concerning the following question:

Hi,
I'm trying to call a perl script within a perl script...but I want to loop this process by calling this script for values stored in an array (I'm basically getting the names of all the files within /home/com/begp/testinvs and storing them in an array as I want to run the script using these filenames as arguments).
Here is my code:
$dirtoget="/home/com/begp/testinvs"; opendir(TIDIR, $dirtoget) || die("Cannot open directory"); @thefiles=readdir(TIDIR); #closedir(TIDIR); foreach $f (@thefiles) { unless ( ($f eq ".") || ($f eq "..") ) { `perl /home/com/begp/test_script.pl` $f || die "Error!"; #This line se +ems to be giving me errors...I'm not sure my syntax is correct here. } }
Thanks very much!

Replies are listed 'Best First'.
Re: calling a perl script within a perl script
by sk (Curate) on Nov 27, 2005 at 02:29 UTC
    `perl /home/com/begp/test_script.pl` $f || die "Error!";

    You need to change that to -

    `perl /home/com/begp/test_script.pl $f` || die "Error!"; # Note the $f position before the backtick.

    Also, your  || die is tricky. If your test_script.pl returns a zero then it will die. backticks return something so use it. Also consider using system and check for the return on the system call.

    An example of the return problem -

    [sk] % cat prifile #!/usr/bin/perl -w print @ARGV; # print whatever is sent. [sk] % cat callprifile #!/usr/bin/perl -w my @files = glob ('*'); push (@files, "0"); # adding a zero as the last element in this array +to demonstrate return issue foreach $f (@files) { `prifile $f` || die "something went wrong with $f\n"; } [sk] % callprifile something went wrong with 0
      Also, your || die is tricky. If your test_script.pl returns a zero then it will die.
      It's actually even trickier. Since the backticks will return the output of the script, if the script doesn't output anything, the main program will die. system is definitely better than backticks if you don't care about the output.

      If you care about speed, consider using do 'filename.pl', which will avoid firing up a new Perl interpreter. You may have to set @ARGV manually to make this work.

      `perl /home/com/begp/test_script.pl $f` || die "Error!";
      I have changed the positioning of the backtick...but it still errors. Also, the test_script does not return a zero.
        You got to give us more information on the error (can you post the error?). if you are talking about your die erroring out then you you need to check if your program (test_script.pl) worked or not before you debug the one you posted. The code you had and the one i posted work just fine for me (with the modification).

        Also couple of things -

        1. Can you run your test script on the command line with an argument does it work? can you do that with a * (shell wild-card) does it work?

        2. Do something like  my $ret = `perl test_script.pl $f`; and then print out $ret for each file.

        Make sure you have  use warnings

        Hope this helps.

        -SK

        Update: Thanks sgifford! It is a good point and I think that could be the reason why the script is failing.

        sbp, Actually tho, i am wondering why would you even need this wrapper script? Can't you just do test_script.pl * on the directory you want?

Re: calling a perl script within a perl script
by jarich (Curate) on Nov 27, 2005 at 13:35 UTC

    I get the impression that you're approaching this problem from a shell scripting perspective. There's nothing wrong with this, but there may be other more elegant solutions you're overlooking.

    For example, can you rewrite test_script.pl into a module which provides a subroutine you can then call for each filename? If so, this code would become a whole lot easier. Don't be scared of subroutines and modules, they're a lot faster than forking off processes which is what this code is doing.

    It's hard to give you a good solution while test_script.pl remains a black box. Does it return input that you need, what does it return upon failure? Answers to these kind of questions can help us a great deal.

    I think you'll find that the following code does what you have asked for, but I'm not convinced that it's actually the correct solution.

    #!/usr/bin/perl -w use strict; # any other setup... my $test_script = "/home/com/begp/test_script.pl"; my $dirtoget="/home/com/begp/testinvs"; # Get all the files which end with .txt (or as appropriate) my @files = glob("$dirtoget/*.txt") foreach my $file (@files) { # Skip if it's a directory or other "non-file" next unless -f $file; my $return = `/usr/bin/perl -w $test_script $file`; # Check if there was an error if($?) { if($? == -1) { die "Failed to run program: $!"; } else { die "Exit value was: " . ($? >>8); } } # Else do whatever is required with the return value # in $return }

    I use glob to ensure that you don't end up accidently working with files which aren't the kind you're looking for. Likewise, just in case files (or directories) are misnamed I also check that the file is a "normal file" which rules out directories, devices, streams etc.

    Be aware that this code can open you to security problems. For example if evilly named files are inserted into that directory, bad things can happen. For example, consider the following possible file name:

    aa; rm -rf /; cat foo.txt

    As things in backticks *always* go past the shell this will be interpretted as if you'd typed on your command line:

    %> /usr/bin/perl -w /home/com/begp/test_script.pl aa %> rm -rf / %> cat foo.txt

    Obviously this is a bad thing and my tests above will not stop this from happening.

    If you can rewrite test_script.pl into a module which provides a subroutine which does the work you need, then that's almost certainly the way to go. If not, turn on Taint mode and check your filenames explicitly.

    Hope this helps

    jarich
Re: calling a perl script within a perl script
by sweetblood (Prior) on Nov 27, 2005 at 02:22 UTC
    Check out fork

    HTH

    Sweetblood

Re: calling a perl script within a perl script
by serf (Chaplain) on Nov 27, 2005 at 10:28 UTC
    Is this really what you want to do?

    Is the script you're calling big and unchangeable? If it's only a small one wouldn't it be better to include its code into your script?

    If it's a big one, can you clone it and make a copy that's got this loop around its functionality?

    If you're running an external command using backticks `` like that - firstly as sk pointed out you have put the filename outside the backticks, so it won't be provided as an argument to the script you're running, but I wouldn't recommend using backticks in the first place.

    If you really must shell out to an external program you're better to use:

    system("...") && die "Can't run ...: $!\n";
    or
    system("...") == 0 || die "Can't run '...' [$?]: $!\n"
    or
    open("... |") || die "Can't run ...: $1\n";
    if you want the output from it - this way the operating system is going to report back to your script whether the program you shelled out to returned an error or a success.

    `` is easy to use but generally sloppy in that it says something like "go away and try and run this, I don't really care if it works or not, but just give me back whatever came back from STDOUT (I don't care about STDERR either)" - which probably isn't what you want your program to do!

    Update:

    Thanks sauoq for pointing out the obvious mistake - NB system() unlike open() returns the output of the program that you ran modified by some rules (which sauoq has expanded below)

    Sorry I rushed out my answer as I was racing out the door running late for a conference!

      system("...") || die "Can't run ...: $!\n";

      No, that's almost never what you would want because system(), unlike qx(), returns the exit status which is conventionally 0 on success.

      It usually better to be explicit with your system() calls:

      if (system( ... ) == 0) { # it succeeded. } else { # it failed. my $exit_val = $? >> 8; my $signal = $? & 127; my $coredump = $? & 128; }

      I should note that some people much prefer to write that clause as if (0 == system( ... )) and will go into violent spasms if they see it written as above. Of course, they've probably fallen out of their chairs by now, so we don't have to worry about them too much.

      -sauoq
      "My two cents aren't worth a dime.";
      

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://511969]
Approved by sk
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others studying the Monastery: (7)
As of 2024-04-23 10:07 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found