class Proc::Async
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 $file, âand\nCamelia\nâĄ\nme\nâ;
my $proc = Proc::Async.new: :w, âtacâ, â--â, $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(12, 42));
$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, :$arg0,
:$win-verbatim-args = False,
:$started = False --> Proc::Async:D)
multi method new( :$path, :@args, :$w, :$enc, :$translate-nl, :$arg0,
:$win-verbatim-args = False,
:$started = False --> 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
.
If :arg0
is set to a value, that value is passed as arg0 to the process
instead of the program name.
The :started
attribute is set by default to False
, so that you need to
start the command afterwards using
.start. You probably don't want to do this
if you want to bind any of the handlers, but it's OK if you just need to
start an external program immediately.
On Windows the flag $win-verbatim-args
disables all automatic quoting of
process arguments. See
this blog
for more information on windows command quoting. The flag is ignored on all
other platforms. The flag was introduced in Rakudo version 2020.06 and is not
present in older releases. By default, it's set to False
, in which case
arguments will be quoted according to Microsoft convention.
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
.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
.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 --> Promise)
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() $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 put
method put(Proc::Async:D: \x, |c)
Does a .join
on the output, adds a newline, and calls .print
on it.
Will throw if it's not started, or not open for writing.
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
.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
multi method kill(Proc::Async:D: Signal:D \signal = SIGHUP)
multi method kill(Proc::Async:D: Int:D \signal)
multi method kill(Proc::Async:D: Str:D \signal)
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); by default and with no argument, the SIGHUP
signal will
be used.