class Proc::Async {}

Proc::Async 允许您异步运行外部命令,捕获标准输出和错误句柄,并可以选择写入其标准输入。

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;

上面的示例生成以下输出

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

或者,您可以使用 Proc::Async,而无需使用 react

# 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.";

这会产生以下输出

Starting...
Output: foo bar
Done.

打开外部程序进行写入的示例

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

管道多个命令的示例,如 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;

方法§

方法 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)

使用外部程序名称或路径 $path 和命令行参数 @args 创建一个新的 Proc::Async 对象。

如果 :w 传递给 new,则会打开一个管道到外部程序的标准输入流(stdin),您可以使用 writesay 向其写入。

:enc 指定流的 编码(仍可以在各个方法中覆盖),默认为 utf8

如果 :translate-nl 设置为 True(默认值),则特定于操作系统的换行符(例如 Windows 上的 \r\n)将自动转换为 \n

如果 :arg0 设置为一个值,则该值将作为 arg0 传递给进程,而不是程序名称。

:started 属性默认设置为 False,因此您需要使用 .start 在之后启动命令。如果您想绑定任何处理程序,则可能不想这样做,但如果您只需要立即启动外部程序,则没有问题。

在 Windows 上,标志 $win-verbatim-args 禁用所有进程参数的自动引用。有关 Windows 命令引用的更多信息,请参阅 此博客。在所有其他平台上忽略该标志。该标志在 Rakudo 版本 2020.06 中引入,在较旧版本中不存在。默认情况下,它设置为 False,在这种情况下,参数将根据 Microsoft 惯例进行引用。

方法 stdout§

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

返回外部程序的标准输出流的 Supply。如果传递了 :bin,则标准输出将作为 Blob 以二进制形式传递,否则将解释为 UTF-8,解码并作为 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;

在调用 .start 之前,必须调用 stdout。否则将抛出 X::Proc::Async::TapBeforeSpawn 类的异常。

如果未调用 stdout,则根本不会捕获外部程序的标准输出。

请注意,不能在同一对象上同时使用和不使用 :bin 调用 stdout;如果尝试这样做,它将抛出 X::Proc::Async::CharsOrBytes 类型的异常。

对于合并的 STDOUT 和 STDERR,请使用 .Supply

方法 stderr§

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

返回外部程序的标准错误流的 Supply。如果传递了 :bin,则标准错误将作为 Blob 以二进制形式传递,否则将解释为 UTF-8,解码并作为 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;

在调用 .start 之前,必须调用 stderr。否则将抛出 X::Proc::Async::TapBeforeSpawn 类的异常。

如果未调用 stderr,则根本不会捕获外部程序的标准错误流。

请注意,不能在同一对象上同时使用和不使用 :bin 调用 stderr;如果尝试这样做,它将抛出 X::Proc::Async::CharsOrBytes 类型的异常。

对于合并的 STDOUT 和 STDERR,请使用 .Supply

方法 bind-stdin§

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

将句柄(必须打开)或 Pipe 设置为 STDIN 的源。目标进程的 STDIN 必须可写,否则将抛出 X::Proc::Async::BindOrUse

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

这等同于

cat < /etc/profile

并将 /etc/profile 的内容打印到标准输出。

方法 bind-stdout§

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

将目标进程的 STDOUT 重定向到句柄(必须打开)。如果 STDOUT 已关闭,则将抛出 X::Proc::Async::BindOrUse

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

此程序将 ls shell 命令的输出通过管道传输到名为 ls.out 的文件,该文件已打开以供读取。

方法 bind-stderr§

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

将目标进程的 STDERR 重定向到句柄(必须打开)。如果 STDERR 已关闭,则将抛出 X::Proc::Async::BindOrUse

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

方法 w§

method w(Proc::Async:D:)

如果将 :w 传递给构造函数,即如果外部程序以其输入流启动,则该流可通过 .print.say.write 方法输出到程序,则返回真值。

方法 start§

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

启动外部程序的生成。返回一个 Promise,该程序将在外部程序退出后使用 Proc 对象保留,如果无法启动程序,则会中断。或者,你可以使用调度程序代替默认的 $*SCHEDULER,或通过命名参数 :$ENV 更改进程运行的环境,或通过命名参数 :$cwd 更改目录。

如果在 Proc::Async 对象上调用 start,而之前已经调用过它,则会抛出类型为 X::Proc::Async::AlreadyStarted 的异常。

注意:如果你希望 await Promise 并丢弃其结果,则使用

try await $p.start;

将抛出如果程序以非零状态退出,因为作为 Promise 结果返回的 Proc 在下沉时抛出,在这种情况下,它将在 try 外部下沉。为了避免这种情况,请在 try 内部 自行下沉

try sink await $p.start;

方法 started§

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

在调用 .start 之前返回 False,之后返回 True

方法 ready§

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

返回一个 Promise,该程序将在进程成功启动后保留。如果程序无法启动,则 Promise 将中断。

特定于实现的注释:从 Rakudo 2018.04 开始,返回的 promise 将保存进程 ID (PID)。

方法 pid§

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

等同于 ready

返回一个 Promise,该程序将在进程成功启动后保留。如果程序无法启动,则 Promise 将中断。返回的 promise 将保存进程 ID (PID)。

特定于实现的注释:从 Rakudo 2018.04 开始可用。

方法 path§

method path(Proc::Async:D:)

已弃用,v6.d 起。请改用 command

返回作为第一个参数传递给 new 方法的外部程序的名称和/或路径。

方法 args§

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

已弃用,v6.d 起。请改用 command

返回外部程序的命令行参数,如传递给 new 方法。

方法 command§

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

从 v6.d 起可用。

返回用于此 Proc::Async 对象的命令和参数

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

方法 write§

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

$b 中的二进制数据写入外部程序的标准输入流。

返回一个Promise,该 Promise 将在数据完全进入外部程序的输入缓冲区后被保留。

必须为写入创建Proc::Async对象(使用Proc::Async.new(:w, $path, @args))。否则,将抛出X::Proc::Async::OpenForWriting异常。

在调用方法 write 之前必须调用start,否则将抛出X::Proc::Async::MustBeStarted异常。

方法 print§

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

$str中的文本数据写入外部程序的标准输入流,并将其编码为 UTF-8。

返回一个Promise,该 Promise 将在数据完全进入外部程序的输入缓冲区后被保留。

必须为写入创建Proc::Async对象(使用Proc::Async.new(:w, $path, @args))。否则,将抛出X::Proc::Async::OpenForWriting异常。

在调用方法 print 之前必须调用start,否则将抛出X::Proc::Async::MustBeStarted异常。

方法 put§

method put(Proc::Async:D: \x|c)

对输出执行.join,添加一个换行符,并对其调用.print。如果未启动或未打开以进行写入,则会抛出异常。

方法 say§

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

$output调用方法gist,添加一个换行符,将其编码为 UTF-8,并将其发送到外部程序的标准输入流,并将其编码为 UTF-8。

返回一个Promise,该 Promise 将在数据完全进入外部程序的输入缓冲区后被保留。

必须为写入创建Proc::Async对象(使用Proc::Async.new(:w, $path, @args))。否则,将抛出X::Proc::Async::OpenForWriting异常。

在调用方法 say 之前必须调用start,否则将抛出X::Proc::Async::MustBeStarted异常。

方法 Supply§

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

返回合并的stdoutstderr流的Supply。如果提供了:$bin命名参数,则Supply将是二进制的,生成Buf对象;否则,它将处于字符模式,生成Str对象,并且:$enc命名参数可以指定要使用的编码:$translate-nl选项指定是否应转换新行结尾以匹配当前操作系统使用的结尾(例如,Windows 上的\r\n)。

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

创建二进制和非二进制.Supply是错误的。同时使用.Supplystderrstdout供应也是错误的。

方法 close-stdin§

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

关闭外部程序的标准输入流。从 STDIN 读取的程序通常只有在其输入流关闭时才会终止。因此,如果等待来自.start的 Promise 挂起(对于为写入打开的程序),则可能是忘记了close-stdin

必须为写入创建Proc::Async对象(使用Proc::Async.new(:w, $path, @args))。否则,将抛出X::Proc::Async::OpenForWriting异常。

在调用方法 close-stdin 之前必须调用start,否则将抛出X::Proc::Async::MustBeStarted异常。

方法 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)

向正在运行的程序发送信号。该信号可以是信号名称(“KILL”或“SIGKILL”)、整数(9)或Signal枚举的元素(Signal::SIGKILL);默认情况下,如果没有参数,将使用SIGHUP信号。