Computing: DOS, OS/2 & Windows Programming

Running a Perl script from within another Perl script.

Please, note, that the examples of this tutorial have been tested on Microsoft Windows; they may not apply as such, or not apply at all to macOS and Linux. I actually use Strawberry Perl 5.32, 64-bit; not sure if all examples apply to other Perl distributions.

I guess that it is rather rare that you need to run a Perl script from another Perl script, but sometimes you may need to do it, or want to do it because, for example, it simplifies things.

Calling a script using exec().

Here are two simple test scripts, called respectively script1.pl and script2.pl.

    # Script script1.pl
    use strict; use warnings;
    print "Script 1 started\n";
    print "Running script 2\n";
    exec('script2.pl');
    print "Script 1 terminated\n";

    # Script script2.pl
    use strict; use warnings;
    print "Script 2 started\n";
    print "Script 2 terminated\n";

Running this script in Windows Command Prompt, typing perl script1.pl results in the following output:

Perl from within Perl: The result of using exec('script.pl')

The reason why the last statement of script1.pl is never executed is because exec() also terminates the script immediately. And why the second script isn't actually executed? Because exec() actually launches the application associated with the extension of the filename passed as parameter. Thus, in my case Komodo Edit is started and the file script2.pl is opened in the editor! Using exec() this way has no practical sense with a Perl file, but is, for example, an easy way to display some document or picture, or to play an audio or video file from within a Perl program.

Calling a script using system().

Lets change the code of script1.pl as follows:

    # Script script1.pl
    use strict; use warnings;
    print "Script 1 started\n";
    print "Running script 2\n";
    system('script2.pl');
    print "Script 1 terminated\n";

Here is the script output:

Perl from within Perl: The result of using system('script.pl')

As exec(), system() opens the application associated with the file's extension. However, after system() has been called, the script continues its execution. In most cases, this is more appropriate than the program termination with exec(). For example, displaying the script's documentation in the web browser, by opening some .html file, but still continuing the calling script.

Note: I have seen in some forum that on Windows, the command system('start script2.pl'); could be used to launch a Perl script from within a Perl script. This is not the case on my system, where it has the same effect as without "start", i.e. launching the extension associated application.

It is, however, possible to use system() - as well as exec() - to launch the second script. In fact, if we pass an executable as parameter, than this one will be run. So, all we have to do is call system() with the Perl binary as first, and the script name as second argument.. Here is the modified code of script1.pl (using the special variable $^X, that contains the full path name of the Perl interpreter).

    # Script script1.pl
    use strict; use warnings;
    print "Script 1 started\n";
    print "Running script 2\n";
    system($^X, 'script2.pl');
    print "Script 1 terminated\n";

And the script output:

Perl from within Perl: Running a script using system()

We can pass arguments to the called script by simply adding them as third, fourth, etc argument of the system() function. Here is the code of the 2 scripts, with passage of one argument from script1.pl to script2.pl.

    # Script script1.pl
    use strict; use warnings;
    print "Script 1 started\n";
    print "Running script 2\n";
    system($^X, 'script2.pl', 'Hello!');
    print "Script 1 terminated\n";

    # Script script2.pl
    use strict; use warnings;
    print "Script 2 started\n";
    my $arg = $ARGV[0];
    print "Script 1 passed me the argument: $arg\n";
    print "Script 2 terminated\n";

And the code for the same with several arguments.

    # Script script1.pl
    use strict; use warnings;
    print "Script 1 started\n";
    print "Running script 2\n";
    my @args = ('Hello there!', 'How are you?', 'I hope you are fine!');
    system($^X, 'script2.pl', @args);
    print "Script 1 terminated\n";

    # Script script2.pl
    use strict; use warnings;
    print "Script 2 started\n";
    if (scalar @ARGV) {
        print "Script 1 passed me the arguments:\n";
        foreach (@ARGV) {
            print "  $_\n";
        }
    }
    print "Script 2 terminated\n";

And here is the output of the scripts with the "several arguments" code above:

Perl from within Perl: Running a script using system() - Passing arguments

In fact, system() is a function, that returns the exit code resulting from the call. This allows us to take action depending on the called script having terminated successfully or not, and even depending on the problem that caused the abnormal termination of the called script. But, that's not all. In the called script, we have the possibility to set a custom exit code for some custom situation, e.g. if the script awaits arguments and there aren't any passed, or if the wrong number of arguments, or invalid arguments are passed. All we have to do is to terminate the called script with the instruction exit(<return-code>).

Here is a new version of our two scripts. The program script1.pl checks the exit code of the system() call and displays a message accordingly to what happened in the called script. And the program script2.pl sets the exit code to 100 in the case where no arguments are passed.

    # Script script1.pl
    use strict; use warnings;
    print "Script 1 started\n";
    print "Running script 2\n";
    my @args = ('Hello there!', 'How are you?', 'I hope you are fine!');
    #my $ret = system($^X, 'script2.pl', @args);
    my $ret = system($^X, 'script2.pl');
    my $mess = check_retcode($ret);
    if ($mess) {
        print "$mess\n";
    }
    print "Script 1 terminated";
    sub check_retcode {
        my ($rc) = @_; my $mess = '';
        if ($rc == -1) {
            $mess = "The system call encountered a problem: $!";
        }
        elsif (my $s = $rc & 127) {
            $mess = "The system call died from signal $s";
        }
        elsif (my $e = $rc >> 8) {
            $mess = "The system call aborted with exit code $e";
        }
        return $mess;
    }

    # Script script2.pl
    use strict; use warnings;
    print "Script 2 started\n";
    if (scalar @ARGV) {
        print "Script 1 passed me the arguments:\n";
        foreach (@ARGV) {
            print " $_\n";
        }
    }
    print "Script 2 terminated\n";
    unless (scalar @ARGV) {
        exit(100);
    }

Here is the output of the scripts, at the first run, passing 3 arguments, at the second run without passing arguments:

Perl from within Perl: Running a script using system() - Testing the exit code

Perhaps, the code of the "exit code checking" subroutine in script1,pl makes you ask "What's that?". Exit codes are a rather complex topic, that I will not discuss here. If you want to understand the details, have a look at the description of the system() function at MetaCPAN, or some Perl book. If not, note the following (and just copy the code...): If the exit code is -1, the call to system itself encountered an error (maybe that this is for Unix-like systems only?). If the result of exitcode & 127 is not 0, the Perl interpreter or the called script (?) died from some signal (again, this may be for Unix-like systems only?). Finally, if exitcode >> 8 is different from 0, something went wrong. On my system, I get an exit code 1, if the Perl interpreter is not found, and an exit code 2 if the called script is not found. An error in the called script (or the abortion of the script, for example by a die instruction) results in exit code 255.

Note: You can also directly access the exit code by reading the content of the special variable $?. In fact the exit code is given by $? >> 8 (cf. example further down in the text).

Catching the script output using capture().

In all examples viewed so far, the output of script2.pl is directed to STDOUT, Perl error messages to STDERR, i.e. everything, that is printed, is printed onto the screen. Using the function capture() of the module IPC::System::Simple (that is normally included by default with the Strawberry Perl distribution), the output of the called script can be caught in the calling script, provided that this script terminated normally. If the called script returned an exit code other than 0, the calling script is aborted with an error message.

Here is the new code script1.pl (the code of script2.pl being the same as in the example before).

    # Script script1.pl
    use strict; use warnings;
    use IPC::System::Simple qw(
        capture
    );
    print "Script 1 started\n";
    print "Running script 2\n\n";
    my @args = ('Hello there!', 'How are you?', 'I hope you are fine!');
    my $output = capture($^X, 'script2.pl', @args);
    #my $output = capture($^X, 'script2.pl');
    print "$output\n";
    print "Script 1 terminated";

Here is the output of the scripts, at the first run, passing 3 arguments, at the second run without passing arguments:

Perl from within Perl: Running a script using capture()

The module IPC::System::Simple includes the variable $EXITVAL that is set to the exit code of the system (capture) call. But, how can we check this value if the calling script aborts if the exit code isn't 0? The capture() function has a second form, with an additional argument (actually being the first argument to be specified), that defines a list (array) of allowed exit codes. In other words, for all exit codes specified in this list, the calling script will continue running, and the exit code returned can be read from $EXITVAL.

In this new version of script1.pl, we allow script2.pl to return exit codes 0 and 100. If the exit code is 0, we print the output of script2.pl, otherwise we print an error message.

    # Script script1.pl
    use strict; use warnings;
    use IPC::System::Simple qw(
        capture $EXITVAL
    );
    print "Script 1 started\n";
    print "Running script 2\n\n";
    my @args = ('Hello there!', 'How are you?', 'I hope you are fine!');
    my $output = capture([0, 100], $^X, 'script2.pl', @args);
    #my $output = capture([0, 100], $^X, 'script2.pl');
    if ($EXITVAL == 0) {
        print "$output\n";
    }
    else {
        print "Script 2 aborted with exit code $EXITVAL\n";
    }
    print "Script 1 terminated";

Here is the output of the scripts, at the first run, passing 3 arguments, at the second run without passing arguments:

Perl from within Perl: Running a script using capture() - Checking $EXITVAL

What happens if, for example, the called script is not found, or if it contains an error? In such cases, first, the called script (or the OS) prints the error message to STDERR (the screen), second, the calling script aborts with an error message indicating the actual exit code (2 if the called script doesn't exist, 255 if the called script includes some Perl syntax error). Unless we allow these exit codes, and handle them in script1.pl (that continues running).

Note: The module IPC::System::Simple includes several other possibilities to run a command from within a Perl script. For details, please, visit the module page at MetaCPAN.

Other ways to run a script from within a script.

As with everything in Perl, there are several ways to do things. Here are some further possibilities to run a Perl script from within another one.

The Perl backtick operator (`) is actually used to run a system command. Specifying the Perl interpreter followed by the name of a Perl script as "backtick string" allows to run this script. You may also specify a scalar or array variable to pass arguments. The result of the operation is the script output (but not as expected; cf. below). Reading the content of the special variable $? allows to check the exit code.

Here is the code of the "backtick version" of script1.pl:

    # Script script1.pl
    use strict; use warnings;
    print "Script 1 started\n";
    print "Running script 2\n\n";
    my @args = ('Hello there!', 'How are you?', 'I hope you are fine!');
    my $output = `$^X script2.pl @args`;
    #my $output = `$^X script2.pl`;
    if ($? == 0) {
        print "$output\n";
    }
    else {
        my $err = $? >> 8;
        print "Script 2 aborted with exit code $err\n";
    }
    print "Script 1 terminated";

Here is the output of the scripts, at the first run, passing 3 arguments, at the second run without passing arguments:

Perl from within Perl: Running a script using the backtick operator - Checking $?

As you can see on the screenshot, this way to proceed is not really useful if you pass string arguments containing spaces to the called script. I think that the space is considered as argument separator, so instead of passing sentences, a series of words is passed (?). This would explain the CR-LF after each word of the argument strings, when printing the script output in the calling script.

Other possibility: In the simple case where there are no parameters to pass and where you are not interested in the output, you can use the Perl instruction do. Syntax:
    do './script2.pl';

Using do, if the called script is terminated with an exit() instruction specifying an exit code other than 0, the calling script will also be terminated...

Yet another possibility is to read the content of script2.pl into a string or list of strings and then using the instruction eval to evaluate the string(s).


If you find this text helpful, please, support me and this website by signing my guestbook.