我想创建一个简单的IO对象,它代表一个向另一个程序打开的管道,我可以在应用运行时定期写入另一个程序的STDIN。我希望它是防弹的(因为它捕获所有错误)和跨平台的。我能找到的最佳选择是:
open
sub io_read {
local $SIG{__WARN__} = sub { }; # Silence warning.
open my $pipe, '|-', @_ or die "Cannot exec $_[0]: $!\n";
return $pipe;
}
优点:
- 跨平台
- 简单
缺点
- 没有
$SIG{PIPE}
来捕获管道程序中的错误 - 是否发现其他错误?
IO::Pipe
sub io_read {
IO::Pipe->reader(@_);
}
优点:
- 简单
- 返回OO接口的IO::Handle对象
- 支持Perl核心。
缺点
- 仍然没有
$SIG{PIPE}
来捕获管道程序中的错误 - Win32不支持(或者至少跳过its tests)
IPC::Run
在IPC::Run中没有用于写入文件句柄的接口,只能追加到标量。这很奇怪。
IPC::Run3
这里也没有文件句柄接口。我可以使用一个代码引用,它会被反复调用以spool到子对象,但是查看源代码,它似乎实际上写入了一个临时文件,然后打开它并将 * 其 * 内容spool到管道命令的STDIN
。
IPC::Cmd
仍然没有文件句柄接口。
我错过了什么?看起来这应该是一个解决的问题,我有点震惊,它不是。IO::Pipe最接近我想要的,但缺乏$SIG{PIPE}
错误处理和缺乏对Windows的支持是令人沮丧的。
3条答案
按热度按时间zqry0prt1#
感谢@ikegami的指导,我发现在Perl中交互式阅读和写入另一个进程的最佳选择是IPC::Run。然而,它要求您正在阅读和写入的程序在完成写入其STDOUT时具有已知输出,例如提示符。下面是一个执行
bash
的示例,让它运行ls -l
,然后打印输出:因为它是作为IPC运行的(我假设),
bash
在完成对STDOUT的写入时不会发出提示。(通过调用echo __END__
)。我还在调用new_chunker
后使用了一个匿名子例程来将输出收集到一个数组中,而不是标量(如果需要,只需将标量的引用传递给'>'
)。所以这是可行的,但在我看来,这有很多原因:
__END__
的文件,上帝会禁止的)。如果我控制一个数据库客户端,我可能必须发送类似SELECT 'IM OUTTA HERE';
的东西。不同的应用程序需要不同的new_appender
hack。$in
和$out
标量感觉很奇怪,而且是远程操作。我不喜欢它。new_chunker
获得面向行的输出的能力很好,如果仍然有点奇怪的话。这在从程序阅读输出时恢复了一些效率,假设IPC::Run有效地缓冲了它。我现在意识到,虽然IPC的接口::Run可能会更好一点,总体而言,IPC模型的弱点尤其使其难以处理。没有通用的IPC接口,因为人们必须知道太多关于正在运行的特定程序的细节才能使其工作。这是好的,* 也许 *,如果你确切地知道它将如何对输入做出React,并且可以可靠地识别何时完成输出,也不需要担心跨平台的兼容性,但这远远不足以满足我对一种通用的方式来与各种数据库命令进行交互的需求-CPAN模块中的线路客户端,可以分布到整个操作系统主机。
最后,感谢a blog post评论中的打包建议,我决定放弃使用IPC来控制这些客户端,而是使用the DBI。它提供了一个优秀的API,健壮,稳定和成熟,并且没有IPC的缺点。
我对那些在我之后的人的建议是:
0sgqnhkj2#
我也做过类似的事情。虽然这取决于父程序和你试图传递的东西。我的解决方案是生成一个子进程(保留$SIG{PIPE}运行)并将其写入日志,或者以你认为合适的方式处理错误。我使用POSIX来处理我的子进程,并且能够利用父进程的所有功能。但是,如果你试图让子进程你有一个主程序的例子和你想管道的东西吗?
vdgimpew3#
可以从(几乎)任意数量的子进程中重定向标准输出和标准错误,并由
select()
触发异步读取它们。子进程不必像接受的答案那样合作,尽管如果它们使用行缓冲或无缓冲I/O会有所帮助。对于POSIX/Un*x,您只需使用
pipe()
,fork()
,POSIX::dup()
,exec()
和select()
的常规组合,或者您提到的更高级别的模块之一,如IPC::Open3()
,IPC::Run()
等。所有这些都不能在Windows下工作,因为
fork()
和exec()
的Perl模拟不足以完成这项任务,而select()
只能在套接字上工作。对于Windows,你需要做的是:
1.创建一个
socketpair()
(使用Perl的socketpair()
模拟)。1.使用
$true = 1; ioctl $child_stdout, 0x8004667e, \$true
在套接字对的读端启用非阻塞I/O。dup()
STDOUT
并保存重复的描述符。dup()
套接字对的写入端替换STDOUT
。1.使用Win32::Process::Create()创建子进程,而不是使用
fork()
和exec()
。1.恢复
STDOUT
。1.等待子进程用
select()
和sysread()
从套接字对的读端写入(分别为flush()
)到标准输出。STDERR
的过程是相同的。对于STDIN
,你必须交换读端和写端。常量
0x8004667e
在Windows头文件中称为FIONBIO
,但在Perl中不可用。我已经写了一篇博客文章,里面有更深入的信息:http://www.guido-flohr.net/platform-independent-asynchronous-child-process-ipc/。可以在这里找到一个包含Perl和C语言工作代码的git仓库:https://github.com/gflohr/platform-independent-ipc。