class Proc

Running process (filehandle-based interface)
class Proc {}

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|Subroutines,run subroutine:

my $proc = run 'echo', 'Hallo world', :out;
my $captured-output = $proc.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 $p1 = run 'echo', 'Hello, world', :out;
my $p2 = run 'cat', '-n', :in($p1.out), :out;
say $p2.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 $p = run "cat", "-n", :in, :out;
$p.in.say: "Hello,\nworld!";
$p.in.close;
say $p.out.slurp: :close;
# OUTPUT: «1  Hello,␤
#          2  world!␤»

In order to capture the standard error, :err can be supplied:

my $p = run "ls", "-l", ".", "qqrq", :out, :err;
my $captured-output = $p.out.slurp: :close;
my $captured-error  = $p.err.slurp: :close;
my $exit-code       = $p.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 safe 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:U:
        :$in = '-',
        :$out = '-',
        :$err = '-',
        Bool :$bin = False,
        Bool :$chomp = True,
        Bool :$merge = False,
        Str:D :$enc = 'UTF-8',
        Str:D :$nl = "\n",
    --> Proc:D)

sub shell(
        $cmd,
        :$in = '-',
        :$out = '-',
        :$err = '-',
        Bool :$bin = False,
        Bool :$chomp = True,
        Bool :$merge = False,
        Str:D :$enc = 'UTF-8',
        Str:D :$nl = "\n",
        :$cwd = $*CWD,
        Hash() :$env = %*ENV
    --> Proc:D)

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 $p-name = "/tmp/program.raku";
my $program = Q:to/END/;
    #!/usr/bin/env raku

    $*OUT.say( qq/\t$*PROGRAM: This goes to standard output/ );
END

spurt $p-name, $program;

$*OUT.put: "1. standard output before doing anything weird";

{
    temp $*OUT = open '/tmp/out.txt', :w;
    $*OUT.put: "2. temp redefine standard output before this message";
    shell( "raku $p-name" ).so;
}

$*OUT.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(*@args ($, *@), :$cwd = $*CWD, Hash() :$env = %*ENV, :$arg0,
                 :$win-verbatim-args = False --> Bool:D)

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($cmd, :$cwd = $*CWD, :$env --> Bool:D)

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:D: --> List:D)

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:D:)

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:D: --> Int:D)

Returns the exit code of the external process, or -1 if it has not exited yet.

method signal

method signal(Proc:D:)

Returns the signal number with which the external process was killed, or 0 or an undefined value otherwise.

See Also

class Attribute

Member variable

class Cancellation

Removal of a task from a Scheduler before normal completion

class Channel

Thread-safe queue for sending values from producers to consumers

class CompUnit

CompUnit

class CompUnit::Repository::FileSystem

CompUnit::Repository::FileSystem

class CompUnit::Repository::Installation

CompUnit::Repository::Installation

class Distro

Distribution related information

class Grammar

Formal grammar made up of named regexes

class IO::ArgFiles

Iterate over contents of files specified on command line

class IO::CatHandle

Use multiple IO handles as if they were one

class IO::Handle

Opened file or stream

class IO::Notification

Asynchronous notification for file and directory changes

class IO::Notification::Change

Changes in a file, produced by watch-file

class IO::Path

File or directory path

class IO::Path::Cygwin

IO::Path pre-loaded with IO::Spec::Cygwin

class IO::Path::Parts

IO::Path parts encapsulation

class IO::Path::QNX

IO::Path pre-loaded with IO::Spec::QNX

class IO::Path::Unix

IO::Path pre-loaded with IO::Spec::Unix

class IO::Path::Win32

IO::Path pre-loaded with IO::Spec::Win32

class IO::Pipe

Buffered inter-process string or binary stream

class IO::Socket::Async

Asynchronous socket in TCP or UDP

class IO::Socket::Async::ListenSocket

A tap for listening TCP sockets

class IO::Socket::INET

TCP Socket

class IO::Spec

Platform specific operations on file and directory paths

class IO::Spec::Cygwin

Platform specific operations on file and directory paths for Cygwin

class IO::Spec::QNX

Platform specific operations on file and directory paths QNX

class IO::Spec::Unix

Platform specific operations on file and directory paths for POSIX

class IO::Spec::Win32

Platform specific operations on file and directory paths for Windows

class IO::Special

Path to special I/O device

class Kernel

Kernel related information

class Lock

A low-level, re-entrant, mutual exclusion lock

class Lock::ConditionVariable

Condition variables used in locks

class Match

Result of a successful regex match

class Pod::Block

Block in a Pod document

class Pod::Block::Code

Verbatim code block in a Pod document

class Pod::Block::Comment

Comment in a Pod document

class Pod::Block::Declarator

Declarator block in a Pod document

class Pod::Block::Named

Named block in a Pod document

class Pod::Block::Para

Paragraph in a Pod document

class Pod::Block::Table

Table in a Pod document

class Pod::Defn

Pod definition list

class Pod::FormattingCode

Pod formatting code

class Pod::Heading

Heading in a Pod document

class Pod::Item

Item in a Pod enumeration list

class Proc::Async

Running process (asynchronous interface)

class Promise

Status/result of an asynchronous computation

class Regex

String pattern

class Semaphore

Control access to shared resources by multiple threads

class Supplier

Live Supply factory

class Supplier::Preserving

Cached live Supply factory

class Supply

Asynchronous data stream with multiple subscribers

class Tap

Subscription to a Supply

class Thread

Concurrent execution of code (low-level)

class ThreadPoolScheduler

Scheduler that distributes work among a pool of threads

class Unicode

Unicode related information

class VM

Raku Virtual Machine related information

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