class Proc::Async {}
注意: 目前只有 Rakudo 的 MoarVM 后端实现了 [Proc::Async](https://docs.raku.org/type/Proc::Async)
。
Proc::Async
允许你异步地运行外部命令, 捕获标准输出和错误句柄,并可选择地写入到它的标准输入中。
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 'lines: ', $_
}
whenever $proc.stderr {
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;
down # 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
或者, 你可以在不使用 react 块的情况下使用 Proc::Async
:
# 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.
打开外部程序进行写入的示例:
use v6;
my $prog = Proc::Async.new(:w, 'hexdump', '-C');
my $promise = $prog.start;
await $prog.write(Buf.new(12, 42));
$prog.close-stdin;
await $promise;
管道几个命令的例子, 例如 echo "Hello, world" | cat -n
:
use v6;
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 --> Proc::Async:D)
multi method new( :$path, :@args, :$w, :$enc, :$translate-nl --> Proc::Async:D)
使用外部程序名或路径 $path
和命令行参数 @args
创建一个新的 Proc::Async
对象。
如果 :w
传递给 new
,则打开一个到外部程序的标准输入流(stdin)的管道,你可以用 write
和 say
来写入。
:enc
指定流的编码(仍然可以在各个方法中重写),默认为 utf8
。
如果 :translate-nl
设置为 True
(默认值),则特定于操作系统的换行终止符(例如 Windows 上的 \r\n
)将自动转换为 \n
。
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类型的异常。
使用 .Supply 用于合并的 STDOUT 和 STDERR。
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 类型的异常。
使用 .Supply 用于合并的 STDOUT 和 STDERR。
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 start(Proc::Async:D: :$scheduler = $*SCHEDULER, :$*CWD --> Promise:D)
开始产生外部程序。返回一个 Promise,一旦外部程序退出,它将与 Proc 对象一起保存,如果程序无法启动,它将被破坏。
如果在之前已调用它的 Proc::Async 对象上调用 start
,则抛出类型 X::Proc::Async::AlreadyStarted的异常。
注意:如果您希望 await
Promise 并丢弃其结果,请使用
try await $p.start;
如果程序以非零状态退出,则抛出,因为 Proc
返回时,Promise 抛出时抛出,在这种情况下它将在 try
之外沉没。为了避免这种情况,请将其自己沉入 try
中:
try sink await $p.start;
started 方法
method started(Proc::Async:D: --> Bool:D)
在 .start
已经被调用之前调用则返回 False
, 否则返回 True
。
ready 方法
method ready(Proc::Asnyc:D: --> Promise:D)
一旦进程成功启动则返回一个 kept 状态的 的 Promise。如果程序无法启动,则 Promise 将被破坏。
特定于实现的注释:从 Rakudo 2018.04开始,返回的 promise 将保存进程 ID(PID)。
pid 方法
method pid(Proc::Async:D: --> Promise:D)
相当于 ready。
一旦进程成功启动则返回一个 kept 状态的 的 Promise。如果程序无法启动,则 Promise 将被破坏。返回的 promise 将保存进程id(PID)。
特定于实现的说明:从 Rakudo 2018.04 开始提供。
path 方法
method path(Proc::Async:D:)
返回作为第一个参数传递给 new
方法的外部程序的名称和/或路径。
args 方法
method args(Proc::Async:D: --> Positional:D)
返回传递给 new
方法的外部程序的命令行参数。
write 方法
method write(Proc::Async:D: Blob:D $b, :$scheduler = $*SCHEDULER --> Promise:D)
将 $b
中的二进制数据写入外部程序的标准输入流。
返回一个 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(Any) $str, :$scheduler = $*SCHEDULER)
将 $str
中的文本数据写入外部程序的标准输入流,将其编码为 UTF-8。
返回一个 Promise,一旦数据完全落在外部程序的输入缓冲区中,它将被保留。
必须创建 Proc::Async
对象以进行写入(使用 Proc::Async.new(:w, $path, @args)
)。否则抛出 X::Proc::Async::OpenForWriting 异常。
必须在调用方法 print 之前调用 start
,否则抛出 X::Proc::Async::MustBeStarted 异常。
say 方法
method say(Proc::Async:D: $output, :$scheduler = $*SCHEDULER)
在 $output
上调用方法 gist
,添加换行符,将其编码为 UTF-8,并将其发送到外部程序的标准输入流,将其编码为 UTF-8。
返回一个 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)
返回合并的 stdout 和 stderr 流的 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: «42100»
whenever .start {}
}
}
创建二进制和非二进制 .Supply 是一个错误。同时使用 .Supply 和 stderr 或 stdout supplies 也是错误的。
close-stdin 方法
method close-stdin(Proc::Async:D:)
关闭外部程序的标准输入流。从 STDIN 读取的程序通常仅在其输入流关闭时终止。因此,如果等待方法启动的承诺挂起(对于为写入而打开的程序),它可能是一个被遗忘的 close-stdin
。
必须创建 Proc::Async
对象以进行写入(使用Proc::Async.new(:w, $path, @args)
)。否则抛出 X::Proc::Async::OpenForWriting 异常。
必须在调用方法 close-stdin
之前调用 start
,否则抛出 X::Proc::Async::MustBeStarted 异常。
kill 方法
method kill(Proc::Async:D: $signal = "HUP")
向正在运行的程序发送信号。信号可以是信号名称(“KILL” 或 “SIGKILL”),整数(9)或 Signal
enum 的元素(Signal::SIGKILL)。