我有一个命令cmd
和三个Vec<u8>
:buff1
,buff2
和buff3
。我想执行cmd
,使用buff1
作为stdin
,并将stdout
捕获为buff2
和stderr
捕获为buff3
。我希望在不显式地编写任何临时文件的情况下完成所有这些。
std::process
似乎允许所有这些事情,只是不是同时发生。
如果我使用Command::new(cmd).output()
,它将返回stdout
和stderr
的缓冲区,但没有办法给它stdin
。
如果我使用Command::new(cmd).stdin(Stdio::piped()).spawn()
然后我可以child.stdin.as_mut().unwrap().write_all(buff1)
但是我不能捕获stdout
和stderr
。
据我所知,没有办法调用Command::new(cmd).stdout(XXX)
来显式地告诉它在缓冲区中捕获stdout
,默认情况下.output()
是这样做的。
看起来像这样的事情应该是可能的:
Command::new(cmd)
.stdin(buff1)
.stdout(buff2)
.stderr(buff3)
.output()
由于Rust使Vec<u8>
看起来像File
,但Vec
不实现Into<Stdio>
我错过了什么吗?是否有办法做到这一点,或者我需要读取和写入实际的文件?
如果您同意使用外部库,subprocess
crate支持以下用例:
let (buff2, buff3) = subprocess::Exec::cmd(cmd)
.stdin(buff1)
.communicate()?
.read()?;
用std::process::Command
做这个比看起来更棘手,因为操作系统不容易将内存区域连接到子进程的stdin。连接文件或任何类似文件的东西很容易,但是要将一块内存提供给子进程,基本上必须在循环中执行write()
。虽然Vec<u8>
确实实现了std::io::Read
,但你不能用它来构造一个实际的File
(或任何其他包含文件描述符/句柄的东西)。
将数据馈送到子进程,同时读取其输出有时被称为通信,参考2004年Python 2.4的新subprocess
模块引入的Python方法。您可以使用std::process
自己实现它,但是您需要小心避免死锁,以防在您尝试为命令提供输入时命令生成输出。(例如,向子进程提供一大块数据,然后读取其标准输出和标准错误的幼稚循环将容易发生此类死锁。)文档描述了一种仅使用标准库安全实现它的可能方法。
如果您希望使用缓冲区进行读写,则需要使用管道形式。原因是,至少在Unix上,进程的输入和输出是通过文件描述符完成的。由于缓冲区本质上不能转换为文件描述符,因此需要使用管道并增量地读取和写入。Rust为缓冲区提供抽象的事实并不能让你避免操作系统没有提供的事实,Rust也不会为你抽象这个。
然而,由于您将使用管道进行读写,因此您需要使用select
之类的东西以避免死锁。否则,当子进程因为需要从标准输出中读取数据而不接受新输入时,您可能会尝试写入。使用select
或poll
(或类似的)允许您确定每个文件描述符何时准备好被读取或写入。在Rust中,这些函数在libc
crate中;我不相信Rust本身提供了这些功能。Windows将有一些类似的功能,但我不知道它是什么。
应该注意的是,除非您确定子进程的输出可以装入内存,否则以更渐进的方式处理它可能会更好。因为你将使用select
,这应该不会太难。