我想创建一个简单的 IO 对象,它代表一个向另一个程序打开的管道,我可以在应用程序运行时定期写入另一个程序的 STDIN。我希望它是防弹的(因为它可以捕获所有错误)并且是跨平台的。我能找到的最佳选择是:
open
sub io_read {
local $SIG{__WARN__} = sub { }; # Silence warning.
open my $pipe, '|-', @_ or die "Cannot exec $_[0]: $!\n";
return $pipe;
}
优点:
缺点
- No
$SIG{PIPE}
捕获管道程序中的错误
- 是否发现其他错误?
IO::Pipe
sub io_read {
IO::Pipe->reader(@_);
}
优点:
- Simple
- 返回 OO 接口的 IO::Handle 对象
- 由 Perl 核心支持。
缺点
- 仍然没有
$SIG{PIPE}
捕获管道程序中的错误
- Win32 不支持(或者至少,它的测试 https://metacpan.org/source/GBARR/IO-1.25/t/io_pipe.t被跳过)
IPC::Run
IPC::Run 中没有用于写入文件句柄的接口,仅附加到标量。这看起来……很奇怪。
IPC::运行3
这里也没有文件句柄接口。我可以使用代码引用,该引用将被重复调用以假脱机到子进程,但查看源代码,它似乎实际上写入临时文件,然后打开它并假脱机its管道命令的内容STDIN
. Wha?
IPC::Cmd
仍然没有文件句柄接口。
我在这里缺少什么?似乎这应该是一个已解决的问题,但令我感到惊讶的是,事实并非如此。 IO::Pipe 最接近我想要的,但缺乏$SIG{PIPE}
错误处理和缺乏对 Windows 的支持令人苦恼。 JDWIM 的管道模块在哪里?
感谢 @ikegami 的指导,我发现在 Perl 中交互式读取和写入另一个进程的最佳选择是 IPC::Run。但是,它要求您正在读取和写入的程序在完成对其 STDOUT 的写入后具有已知的输出,例如提示。这是一个执行的例子bash
,运行了吗ls -l
,然后打印该输出:
use v5.14;
use IPC::Run qw(start timeout new_appender new_chunker);
my @command = qw(bash);
# Connect to the other program.
my ($in, @out);
my $ipc = start \@command,
'<' => new_appender("echo __END__\n"), \$in,
'>' => new_chunker, sub { push @out, @_ },
timeout(10) or die "Error: $?\n";
# Send it a command and wait until it has received it.
$in .= "ls -l\n";
$ipc->pump while length $in;
# Wait until our end-of-output string appears.
$ipc->pump until @out && @out[-1] =~ /__END__\n/m;
pop @out;
say @out;
因为它作为 IPC 运行(我假设),bash
写入 STDOUT 后不会发出提示。所以我用new_appender()
函数让它发出一些我可以匹配的东西来找到输出的结尾(通过调用echo __END__
)。我还在调用后使用了匿名子例程new_chunker
将输出收集到数组中,而不是标量中(只需将对标量的引用传递给'>'
如果你想要的话)。
所以这可行,但在我看来,由于很多原因,它很糟糕:
- 通常没有有用的方法来知道 IPC 控制的程序是否已完成打印到其 STDOUT。相反,您必须在其输出上使用正则表达式来搜索通常意味着已完成的字符串。
- 如果它不发出一个,你必须欺骗它发出一个(就像我在这里所做的那样——上帝保佑我是否应该有一个名为
__END__
, 尽管)。如果我控制数据库客户端,我可能必须发送类似的内容SELECT 'IM OUTTA HERE';
。不同的应用需要不同的new_appender
hacks.
- 书写魔法
$in
and $out
标量感觉很奇怪并且是远距离作用。我不喜欢它。
- 人们无法像文件句柄那样对标量进行面向行的处理。因此,他们的效率较低。
- 使用能力
new_chunker
获得面向行的输出很好,尽管仍然有点奇怪。不过,假设 IPC::Run 有效地缓冲了程序的输出,这会恢复一些从程序读取输出的效率。
我现在意识到,尽管 IPC::Run 的界面可能会更好一点,但总体而言,IPC 模型的弱点尤其使其难以处理。没有通用的 IPC 接口,因为人们必须了解太多正在运行的特定程序的细节才能使其工作。这没关系,maybe,如果您确切地知道它将如何对输入做出反应,并且能够可靠地识别它何时完成发出输出,并且不需要太担心跨平台兼容性。但这还远远不足以满足我对一种普遍有用的方式来与 CPAN 模块中的各种数据库命令行客户端进行交互的需求,该模块可以分发到整个操作系统主机。
最后感谢评论里的打包建议一篇博文 http://www.justatheory.com/computers/databases/dbi-in-sqitch.html,我决定放弃使用 IPC 来控制这些客户端,而使用the DBI http://metacpan.org/module/DBI, 反而。它提供了优秀的API,健壮、稳定、成熟,并且没有IPC的缺点。
我对那些追随我的人的建议是:
- 如果您只需要执行另一个程序并等待它完成,或者在运行完成后收集其输出,请使用IPC::系统::简单 https://metacpan.org/module/IPC%3a%3aSystem%3a%3aSimple。否则,如果您需要与其他东西交互交互,请尽可能使用 API。如果不可能,那么使用类似的东西IPC::Run https://metacpan.org/module/IPC%3a%3aRun并尽力充分利用它,并准备好放弃大量时间来让它“恰到好处”。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)