Proc
is a representation of an invocation of an external process. It provides access to the input, output and error stream as well as the exit code. It is typically created through the run
subroutine:
my = run 'echo', 'Hallo world', :out;my = .out.slurp: :close;say "Output was $captured-output.raku()";# OUTPUT: «Output was "Hallo world\n"»
Piping several commands is easy too. To achieve the equivalent of the pipe echo "Hello, world" | cat -n
in Raku, and capture the output from the second command, you can do
my = run 'echo', 'Hello, world', :out;my = run 'cat', '-n', :in(.out), :out;say .out.get;
You can also feed the :in
(standard input) pipe directly from your program, by setting it to True
, which will make the pipe available via .in
method on the Proc
:
my = run "cat", "-n", :in, :out;.in.say: "Hello,\nworld!";.in.close;say .out.slurp: :close;# OUTPUT: «1 Hello,# 2 world!»
In order to capture the standard error, :err
can be supplied:
my = run "ls", "-l", ".", "qqrq", :out, :err;my = .out.slurp: :close;my = .err.slurp: :close;my = .exitcode;
In sink context, a Proc
will call its sink
method, throwing an exception if the process has exited with an exit code different from zero:
shell 'exit 1'# OUTPUT: «(exit code 1) The spawned command 'exit 1' exited unsuccessfully (exit code: 1)»
Note: Versions of Rakudo older than 2017.04 do not have .slurp
available on IO::Pipe
objects; use .slurp-rest
instead.
Use Proc::Async
for non-blocking operations.
Potential Deadlocks§
If you run an external program with :out
and :err
(so capturing standard output and standard error separately), a deadlock can occur, for example in the following scenario:
Your Raku script reads from the program's standard output until the End of File (EOF) marker).
The external program writes to its standard error stream.
The external program runs into the standard error's buffer limit.
Your Raku script blocks, waiting for input on the output stream, while the external program blocks until its standard error buffer is being drained.
You can avoid this by using :merge
to join the external program's standard output and error streams, so that you only need to read from one pipe. This presupposes that you do not need separate access to the two streams. If you do, the only save approach is to use Proc::Async
.
A similar deadlock can occur when you call an external program with both the :in
option (to open a pipe to its standard input) and one of the :out
, :err
or :merge
options. In this scenario, it can happen that your Raku script blocks reading from the external program's output while it waits for input, and vice versa.
In this scenario, switching to Proc::Async
is the most robust solution.
Methods§
routine new§
method new(Proc:: = '-',: = '-',: = '-',Bool : = False,Bool : = True,Bool : = False,Str : = 'UTF-8',Str : = "\n",--> Proc)sub shell(,: = '-',: = '-',: = '-',Bool : = False,Bool : = True,Bool : = False,Str : = 'UTF-8',Str : = "\n",: = ,Hash() : =--> Proc)
new
creates a new Proc
object, whereas run
or shell
create one and spawn it with the command and arguments provided in @args
or $cmd
, respectively.
$in
, $out
and $err
are the three standard streams of the to-be-launched program, and default to "-"
meaning they inherit the stream from the parent process. Setting one (or more) of them to True
makes the stream available as an IO::Pipe
object of the same name, like for example $proc.out
. You can set them to False
to discard them. Or you can pass an existing IO::Handle
object (for example IO::Pipe
) in, in which case this handle is used for the stream.
Please bear in mind that the process streams reside in process variables, not in the dynamic variables that make them available to our programs. Thus, modifying the dynamic filehandle variables (such as $*OUT
) inside the host process will have no effect in the spawned process, unlike $*CWD
and $*ENV
, whose changes will be actually reflected in it.
my = "/tmp/program.raku";my =spurt , ;.put: "1. standard output before doing anything weird";.put: "3. everything should be back to normal";# OUTPUT# 1. standard output before doing anything weird# /tmp/program.raku: This goes to standard output# 3. everything should be back to normal# /tmp/out.txt will contain:# 2. temp redefine standard output before this message
This program shows that the program spawned with shell
is not using the temporary $*OUT
value defined in the host process (redirected to /tmp/out.txt
), but the initial STDOUT
defined in the process.
$bin
controls whether the streams are handled as binary (i.e. Blob
object) or text (i.e. Str
objects). If $bin
is False, $enc
holds the character encoding to encode strings sent to the input stream and decode binary data from the output and error streams.
With $chomp
set to True
, newlines are stripped from the output and err streams when reading with lines
or get
. $nl
controls what your idea of a newline is.
If $merge
is set to True, the standard output and error stream end up merged in $proc.out
.
method sink§
method sink(--> Nil)
When sunk, the Proc
object will throw X::Proc::Unsuccessful
if the process it ran exited unsuccessfully.
shell 'ls /qqq';# OUTPUT:# (exit code 1) ls: cannot access '/qqq': No such file or directory# The spawned command 'ls /qqq' exited unsuccessfully (exit code: 2)# in block <unit> at /tmp/3169qXElwq line 1#
method spawn§
method spawn(* ($, *@), : = , Hash() : = , :,: = False --> Bool)
Runs the Proc
object with the given command, argument list, working directory, and environment.
If :arg0
is set to a value, that value is passed as arg0 to the process instead of the program name.
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.
method shell§
method shell(, : = , : --> Bool)
Runs the Proc
object with the given command and environment which are passed through to the shell for parsing and execution. See shell
for an explanation of which shells are used by default in the most common operating systems.
method command§
method command(Proc: --> List)
The command method is an accessor to a list containing the arguments that were passed when the Proc object was executed via spawn
or shell
or run
.
method Bool§
multi method Bool(Proc:)
Awaits for the process to finish and returns True
if both exit code and signal of the process were 0, indicating a successful process termination. Returns False
otherwise.
method pid§
method pid()
Returns the PID
value of the process if available, or Nil
.
method exitcode§
method exitcode(Proc: --> Int)
Returns the exit code of the external process, or -1 if it has not exited yet.
method signal§
method signal(Proc:)
Returns the signal number with which the external process was killed, or 0
or an undefined value otherwise.