class Proc::Async

Running process (asynchronous interface)

class Proc::Async {}

Proc::Async allows you to run external commands asynchronously, capturing standard output and error handles, and optionally write to its standard input.

my $file = foo.IO;
spurt $fileand\nCamelia\n\nme\n;
 
my $proc = Proc::Async.new: :wtac--$file-;
# my $proc = Proc::Async.new: :w, ‘sleep’, 15; # uncomment to try timeouts 
 
react {
    whenever $proc.stdout.lines { # split input on \r\n, \n, and \r 
        say line: $_
    }
    whenever $proc.stderr { # chunks 
        say stderr: $_
    }
    whenever $proc.ready {
        say PID: $_ # Only in Rakudo 2018.04 and newer, otherwise Nil 
    }
    whenever $proc.start {
        say Proc finished: exitcode=.exitcode signal=.signal;
        done # gracefully jump from the react block 
    }
    whenever $proc.print: I\n\nCamelia\n {
        $proc.close-stdin
    }
    whenever signal(SIGTERM).merge: signal(SIGINT{
        once {
            say Signal received, asking the process to stop;
            $proc.kill# sends SIGHUP, change appropriately 
            whenever signal($_).zip: Promise.in(2).Supply {
                say Kill it!;
                $proc.kill: SIGKILL
            }
        }
    }
    whenever Promise.in(5{
        say Timeout. Asking the process to stop;
        $proc.kill# sends SIGHUP, change appropriately 
        whenever Promise.in(2{
            say Timeout. Forcing the process to stop;
            $proc.kill: SIGKILL
        }
    }
}
 
say Program finished;

Example above produces the following output:

line: me
line: ♡
line: Camelia
line: and
line: Camelia
line: ♥
line: I
Proc finished. Exit code: 0
Program finished

Alternatively, you can use Proc::Async without using a react block:

# command with arguments 
my $proc = Proc::Async.new('echo''foo''bar');
 
# subscribe to new output from out and err handles: 
$proc.stdout.tap(-> $v { print "Output: $v" }quit => { say 'caught exception ' ~ .^name });
$proc.stderr.tap(-> $v { print "Error:  $v" });
 
say "Starting...";
my $promise = $proc.start;
 
# wait for the external program to terminate 
await $promise;
say "Done.";

This produces the following output:

Starting...
Output: foo bar
Done.

An example that opens an external program for writing:

my $prog = Proc::Async.new(:w'hexdump''-C');
my $promise = $prog.start;
await $prog.write(Buf.new(1242));
$prog.close-stdin;
await $promise;

An example of piping several commands like echo "Hello, world" | cat -n:

my $proc-echo = Proc::Async.new: 'echo''Hello, world';
my $proc-cat = Proc::Async.new: 'cat''-n';
$proc-cat.bind-stdin: $proc-echo.stdout;
await $proc-echo.start$proc-cat.start;

Methods

method new

multi method new(*@ ($path*@args), :$w:$enc:$translate-nl --> Proc::Async:D)
multi method new(   :$path:@args,  :$w:$enc:$translate-nl --> Proc::Async:D)

Creates a new Proc::Async object with external program name or path $path and the command line arguments @args.

If :w is passed to new, then a pipe to the external program's standard input stream (stdin) is opened, to which you can write with write and say.

The :enc specifies the encoding for streams (can still be overridden in individual methods) and defaults to utf8.

If :translate-nl is set to True (default value), OS-specific newline terminators (e.g. \r\n on Windows) will be automatically translated to \n.

method stdout

method stdout(Proc::Async:D: :$bin --> Supply:D)

Returns the Supply for the external program's standard output stream. If :bin is passed, the standard output is passed along in binary as Blob, otherwise it is interpreted as UTF-8, decoded, and passed along as Str.

my $proc = Proc::Async.new(:r'echo''Raku');
$proc.stdout.tap-> $str {
    say "Got output '$str' from the external program";
});
my $promise = $proc.start;
await $promise;

You must call stdout before you call method start. Otherwise an exception of class X::Proc::Async::TapBeforeSpawn is thrown.

If stdout is not called, the external program's standard output is not captured at all.

Note that you cannot call stdout both with and without :bin on the same object; it will throw an exception of type X::Proc::Async::CharsOrBytes if you try.

Use .Supply for merged STDOUT and STDERR.

method stderr

method stderr(Proc::Async:D: :$bin --> Supply:D)

Returns the Supply for the external program's standard error stream. If :bin is passed, the standard error is passed along in binary as Blob, otherwise it is interpreted as UTF-8, decoded, and passed along as Str.

my $proc = Proc::Async.new(:r'echo''Raku');
$proc.stderr.tap-> $str {
    say "Got error '$str' from the external program";
});
my $promise = $proc.start;
await $promise;

You must call stderr before you call method start. Otherwise an exception of class X::Proc::Async::TapBeforeSpawn is thrown.

If stderr is not called, the external program's standard error stream is not captured at all.

Note that you cannot call stderr both with and without :bin on the same object; it will throw an exception of type X::Proc::Async::CharsOrBytes if you try.

Use .Supply for merged STDOUT and STDERR.

method bind-stdin

multi method bind-stdin(IO::Handle:D $handle)
multi method bind-stdin(Proc::Async::Pipe:D $pipe)

Sets a handle (which must be opened) or a Pipe as a source of STDIN. The STDIN of the target process must be writable or X::Proc::Async::BindOrUse will be thrown.

my $p = Proc::Async.new("cat":in);
my $h = "/etc/profile".IO.open;
$p.bind-stdin($h);
$p.start;

This is equivalent to

cat < /etc/profile

and will print the content of /etc/profile to standard output.

method bind-stdout

method bind-stdout(IO::Handle:D $handle)

Redirects STDOUT of the target process to a handle (which must be opened). If STDOUT is closed X::Proc::Async::BindOrUse will be thrown.

my $p = Proc::Async.new("ls":out);
my $h = "ls.out".IO.open(:w);
$p.bind-stdout($h);
$p.start;

This program will pipe the output of the ls shell command to a file called ls.out, which we are opened for reading.

method bind-stderr

method bind-stderr(IO::Handle:D $handle)

Redirects STDERR of the target process to a handle (which must be opened). If STDERR is closed X::Proc::Async::BindOrUse will be thrown.

my $p = Proc::Async.new("ls""--foo":err);
my $h = "ls.err".IO.open(:w);
$p.bind-stderr($h);
$p.start;

method w

method w(Proc::Async:D:)

Returns a true value if :w was passed to the constructor, that is, if the external program is started with its input stream made available to output to the program through the .print, .say and .write methods.

method start

method start(Proc::Async:D: :$scheduler = $*SCHEDULER:$ENV:$cwd = $*CWD)

Initiates spawning of the external program. Returns a Promise that will be kept with a Proc object once the external program exits or broken if the program cannot be started. Optionally, you can use a scheduler instead of the default $*SCHEDULER, or change the environment the process is going to run in via the named argument :$ENV or the directory via the named argument :$cwd.

If start is called on a Proc::Async object on which it has already been called before, an exception of type X::Proc::Async::AlreadyStarted is thrown.

Note: If you wish to await the Promise and discard its result, using

try await $p.start;

will throw if the program exited with non-zero status, as the Proc returned as the result of the Promise throws when sunk and in this case it will get sunk outside the try. To avoid that, sink it yourself inside the try:

try sink await $p.start;

method started

method started(Proc::Async:D: --> Bool:D)

Returns False before .start has been called, and True afterwards.

method ready

method ready(Proc::Async:D: --> Promise:D)

Returns a Promise that will be kept once the process has successfully started. Promise will be broken if the program fails to start.

Implementation-specific note: Starting from Rakudo 2018.04, the returned promise will hold the process id (PID).

method pid

method pid(Proc::Async:D: --> Promise:D)

Equivalent to ready.

Returns a Promise that will be kept once the process has successfully started. Promise will be broken if the program fails to start. Returned promise will hold the process id (PID).

Implementation-specific note: Available starting from Rakudo 2018.04.

method path

method path(Proc::Async:D:)

Deprecated as of v6.d. Use command instead.

Returns the name and/or path of the external program that was passed to the new method as first argument.

method args

method args(Proc::Async:D: --> Positional:D)

Deprecated as of v6.d. Use command instead.

Returns the command line arguments for the external programs, as passed to the new method.

method command

method command(Proc::Async:D: --> List:D)

Available as of v6.d.

Returns the command and arguments used for this Proc::Async object:

my $p := Proc::Async.new: 'cat''some''files';
$p.command.say# OUTPUT: «(cat some files)␤»

method write

method write(Proc::Async:D: Blob:D $b:$scheduler = $*SCHEDULER --> Promise:D)

Write the binary data in $b to the standard input stream of the external program.

Returns a Promise that will be kept once the data has fully landed in the input buffer of the external program.

The Proc::Async object must be created for writing (with Proc::Async.new(:w, $path, @args)). Otherwise an X::Proc::Async::OpenForWriting exception will the thrown.

start must have been called before calling method write, otherwise an X::Proc::Async::MustBeStarted exception is thrown.

method print

method print(Proc::Async:D: Str(Any$str:$scheduler = $*SCHEDULER)

Write the text data in $str to the standard input stream of the external program, encoding it as UTF-8.

Returns a Promise that will be kept once the data has fully landed in the input buffer of the external program.

The Proc::Async object must be created for writing (with Proc::Async.new(:w, $path, @args)). Otherwise an X::Proc::Async::OpenForWriting exception will the thrown.

start must have been called before calling method print, otherwise an X::Proc::Async::MustBeStarted exception is thrown.

method say

method say(Proc::Async:D: $output:$scheduler = $*SCHEDULER)

Calls method gist on the $output, adds a newline, encodes it as UTF-8, and sends it to the standard input stream of the external program, encoding it as UTF-8.

Returns a Promise that will be kept once the data has fully landed in the input buffer of the external program.

The Proc::Async object must be created for writing (with Proc::Async.new(:w, $path, @args)). Otherwise an X::Proc::Async::OpenForWriting exception will the thrown.

start must have been called before calling method say, otherwise an X::Proc::Async::MustBeStarted exception is thrown.

method Supply

multi method Supply(Proc::Async:D: :$bin!)
multi method Supply(Proc::Async:D: :$enc:$translate-nl)

Returns a Supply of merged stdout and stderr streams. If :$bin named argument is provided, the Supply will be binary, producing Buf objects, otherwise, it will be in character mode, producing Str objects and :$enc named argument can specify encoding to use. The :$translate-nl option specifies whether new line endings should be translated for to match those used by the current operating system (e.g. \r\n on Windows).

react {
    with Proc::Async.new: «"$*EXECUTABLE" -e 'say 42; note 100'» {
        whenever .Supply { .print }  # OUTPUT: «42␤100␤» 
        whenever .start {}
    }
}

It is an error to create both binary and non-binary .Supply. It is also an error to use both .Supply and either stderr or stdout supplies.

method close-stdin

method close-stdin(Proc::Async:D: --> True)

Closes the standard input stream of the external program. Programs that read from STDIN often only terminate when their input stream is closed. So if waiting for the promise from method start hangs (for a program opened for writing), it might be a forgotten close-stdin.

The Proc::Async object must be created for writing (with Proc::Async.new(:w, $path, @args)). Otherwise an X::Proc::Async::OpenForWriting exception will the thrown.

start must have been called before calling method close-stdin, otherwise an X::Proc::Async::MustBeStarted exception is thrown.

method kill

method kill(Proc::Async:D: $signal = "HUP")

Sends a signal to the running program. The signal can be a signal name ("KILL" or "SIGKILL"), an integer (9) or an element of the Signal enum (Signal::SIGKILL).

Type Graph

Type relations for Proc::Async
perl6-type-graph Proc::Async Proc::Async Any Any Proc::Async->Any Mu Mu Any->Mu

Expand above chart