Inter-process communication

Programs running other programs and communicating with them

Running programs

Many programs need to be able to run other programs, and we need to pass information to them and receive their output and exit status. Running a program in Raku is as easy as:

run 'git', 'status';

This line runs the program named "git" and passes "git" and "status" to its command-line. It will find the program using the %*ENV<PATH> setting.

If you would like to run a program by sending a command-line to the shell, there's a tool for that as well. All shell metacharacters are interpreted by the shell, including pipes, redirects, environment variable substitutions and so on.

shell 'ls -lR | gzip -9 > ls-lR.gz';

Caution should be taken when using shell with user input.

The Proc object

Both run and shell return a Proc object, which can be used to communicate with the process in more detail. Please note that unless you close all output pipes, the program will usually not terminate.

my $git = run 'git', 'log', '--oneline', :out;
    for $git.out.lines -> $line {
        my ($sha, $subject) = $line.split: ' ', 2;
        say "$subject [$sha]";
    }
    $git.out.close();

If the program fails (exits with a non-zero exit code), it will throw an exception when the returned Proc object is sunk. You can save it into a variable, even anonymous one, to prevent the sinking:

$ = run '/bin/false'; # does not sink the Proc and so does not throw

You can tell the Proc object to capture output as a filehandle by passing the :out and :err flags. You may also pass input via the :in flag.

my $echo = run 'echo', 'Hello, world', :out;
    my $cat  = run 'cat', '-n', :in($echo.out), :out;
    say $cat.out.get;
    $cat.out.close();

You may also use Proc to capture the PID, send signals to the application, and check the exitcode.

my $crontab = run 'crontab', '-l';
    if $crontab.exitcode == 0 {
        say 'crontab -l ran ok';
    }
    else {
        say 'something went wrong';
    }

Example with shell

Suppose there is a utility that will takes input from STDIN and outputs to STDOUT, but there isn't yet a Raku wrapper. For this example, lets use sass, which preprocesses a more convenient form of CSS called SCSS into standard CSS. In a terminal the utility would be used as sass --stdin

We want to convert multiple strings of SCSS into CSS. The code below only converts one string and captures the output into a variable, but it should be obvious how to adapt this into a loop.

    #| process with utility, first check it exists in the environment
    my $proc = shell( <<sass --version>>, :out, :merge);
    exit note 'Cannot run sass' unless $proc.out.slurp(:close) ~~ / \d \. \d+ /;

    # start loop here for multiple SCSS strings
    # set up the process for stdin.
    $proc = shell( <<sass --stdin --style=compressed>>, :in, :err, :out );
    my $scss = q:to/SCSS/;
        div.rendered-formula {
            display: flex;
            justify-content: space-around;
            align-items: center;
            img.logo {
                align-self: center;
            }
        }
        SCSS
    # now pipe the SCSS string into the process as STDIN (remembering to close the pipe)
    $proc.in.spurt($scss,:close);
    # extract the output or the error
    my $error  = $_ with $proc.err.slurp(:close);
    my $output = $_ with $proc.out.slurp(:close);

    # end loop here

The Proc::Async object

When you need more control over the communication with and from another process, you will want to make use of Proc::Async. This class provides support for asynchronous communication with a program, as well as the ability to send signals to that program.

# Get ready to run the program
my $log = Proc::Async.new('tail', '-f',  '/var/log/system.log');
$log.stdout.tap(-> $buf { print $buf });
$log.stderr.tap(-> $buf { $*ERR.print($buf) });
# Start the program
my $done = $log.start;
sleep 10;
# Tell the program to stop
$log.kill('QUIT');
# Wait for the program to finish
await $done;

The small program above uses the "tail" program to print out the contents of the log named system.log for 10 seconds and then tells the program to stop with a QUIT signal.

Whereas Proc provides access to output using IO::Handles, Proc::Async provides access using asynchronous supplies (see Supply).

If you want to run a program and do some work while you wait for the original program to finish, the start routine returns a Promise, which is kept when the program quits.

Use the write method to pass data into the program.

See Also

Classes and objects

A tutorial about creating and using classes in Raku

CompUnits and where to find them

How and when Raku modules are compiled, where they are stored, and how to access them in compiled form.

Concurrency

Concurrency and asynchronous programming

Command line interface

Creating your own CLI in Raku

Grammar tutorial

An introduction to grammars

Input/Output

File-related operations

Iterating

Functionalities available for visiting all items in a complex data structure

Doing math with Raku

Different mathematical paradigms and how they are implemented in this language

Module packages

Creating module packages for code reuse

Core modules

Core modules that may be useful to module authors

Module development utilities

What can help you write/test/improve your module(s)

Modules

How to create, use, and distribute Raku modules

Creating operators

A short tutorial on how to declare operators and create new ones.

Regexes: best practices and gotchas

Some tips on regexes and grammars

REPL

Read-eval-print loop

Entering unicode characters

Input methods for unicode characters in terminals, the shell, and editors

The Camelia image is copyright 2009 by Larry Wall. "Raku" is trademark of the Yet Another Society. All rights reserved.