如何在 perl 中使用标量作为 open3 的输入
Posted
技术标签:
【中文标题】如何在 perl 中使用标量作为 open3 的输入【英文标题】:How can I use a scalar as input to open3 in perl 【发布时间】:2015-10-08 22:07:16 【问题描述】:我有一个标量,我想输入 open3 作为输入。例如
my $sql = "select * from table;";
open( SQL, "<", \$sql );
my ($output);
open3( '<&SQL', $output, $output, "mysql -h 127.0.0.1" );
但是,open3
位于不同的模块中:
package main;
use Example::Runner;
my $sql = "select * from table;";
open( my $in_handle, "<", \$sql );
my ($out_handle);
Example::Runner::run( $in_handle, $out_handle, $out_handle
'mysql -h 127.0.0.1' );
然后在另一个文件中:
package Example::Runner;
sub run
my ($in, $out, $err, @command) = @_;
open3( ?, $out, $err, "mysql -h 127.0.0.1" );
问题是,在Example::Runner
中,我有一个可以从<$in>
读取的参考,但我需要的是我可以在'<&'
前加上前缀,以便open3 将其用作命令的STDIN
它执行。知道如何将句柄的引用转换为 open3 可以用于其STDIN
的东西吗?
编辑:
很明显,我的人为示例还不够……我不直接使用 DBI 的原因是这段代码实际上是我用于 footprintless 自动化。换句话说,我有一个由 30 多台服务器组成的环境,我的管理员没有安装任何特殊工具(只是 RHEL 5/6 中的标准工具)。这些服务器分为服务器集(db、app、web),用于每个环境(本地、dev、qa、beta、prod),用于每个项目(...)。无论如何,一项非常常见的任务是将数据库从一个地方复制到另一个地方。我们使用类似于以下的命令来完成此操作:
use IPC::Open3::Callback::CommandRunner;
use IPC::Open3::Callback::Command qw(command pipe_command);
my $source_config = hostname => 'proj1-prod-db', sudo_username => 'db';
my $dest_config = hostname => 'proj1-prod-db', sudo_username => 'db';
my $command_runner = IPC::Open3::Callback::CommandRunner->new();
$command_runner->run_or_die( pipe_command(
command( "mysqldump dbname", $source_config ),
command( "mysql dbname", $dest_config ) ) );
# runs: ssh proj1-prod-db "sudo -u db mysqldump dbname" | ssh proj1-dev-db "sudo -u db mysql dbname"
这是MOST 将我们的生产数据库克隆回开发环境的基本版本(更典型的版本包括每个命令上的许多开关和中间的许多管道命令)。所以,我围绕这个(IPC::Open3::Callback::*)编写了一个抽象库。在此过程中,我们遇到了需要执行一些在复制数据库后需要运行的 SQL 命令。因此,我们添加了运行任意 SQL 脚本集的功能(基于克隆操作的源和目标)。我可以用这样的命令运行它们:
$command_runner->run_or_die( pipe_command(
"cat $post_restore",
command( "mysql dbname", $dest_config ) ) );
但是我遇到了一些 SQL 脚本内容的需求,所以我想把它吞进去,做一些工作,然后提供给$command_runner
作为STDIN
。也就是说,我尝试使用fileno 来处理这个问题:
sub safe_open3_with
my ($in_handle, $out_handle, $err_handle, @command) = @_;
my @args = (
$in_handle ? '<&' . fileno( $in_handle ) : undef,
$out_handle ? '>&' . fileno( $out_handle ) : undef,
$err_handle ? '>&' . fileno( $err_handle ) : undef,
@command
);
return ( $^O =~ /MSWin32/ ) ? _win_open3(@args) : _nix_open3(@args);
但是如果$in_handle
是一个标量引用,它就不起作用。总之,说来话长。
【问题讨论】:
我不确定我是否理解。为什么你不能给它一个句柄然后打印到那个句柄?my $pid = open3( \*SQL, $output, $output, "mysql -h 127.0.0.1" );if($pid)print SQL (<WHATEVERYOUREADIN>);close(SQL);
或保留您原来的SQL
并执行open (\*SQL2,$output,$output,...);print SQL2 <SQL>; close SQL2;
@hepcat72,@mob,我试图简化这个例子中的问题,但原因是它必须在 在命令开始运行之前或@987654340 @我用来处理 OUT 和 ERR(以及 IN ish)变得很不稳定。 (这是给IO::Open3::Callback::safe_open_3
)...
使用 IPC::Open3 无法做到这一点。使用 IPC::Run 获得更灵活的接口,它还支持输入/输出的标量和函数。
也许我不明白你到底想做什么,但为什么不使用 DBI?
【参考方案1】:
open \$var
不起作用,因为它没有创建孩子可以从中读取的系统文件句柄。
$ perl -E'open(my $fh, "<", \"abc") or die $!; say fileno($fh);'
-1
首先,你需要一个管道。
pipe(local *CHILD_STDIN, local *TO_CHILD)
or die("Can't create pipe: $!\n");
my $pid = open3($cmd, '<&CHILD_STDIN', local *FROM_CHILD, undef);
然后,您将打印mysql
的数据以读取到TO_CHILD
。
print(TO_CHILD do local $/; <$in> );
close(TO_CHILD);
但这很危险。你冒着陷入僵局的风险。 (如果孩子在您尝试向其 STDIN 发送大量[1] 数量时尝试向 STDOUT 或 STDERR 发送大量[1] 则会发生死锁.) 为了避免这个问题,你需要一个select
循环。这很难。你不想使用这么低级别的东西。使用IPC::Run3 或IPC::Run 而不是open3
,因为它们会为您完成所有肮脏的工作。
use IPC::Run3 qw( run3 );
run3($shell_cmd, \$sql, \my $out, \my $err);
更好的是,避免不必要的外壳:
run3([ $prog, @args ], \$sql, \my $out, \my $err);
但您为什么要使用专为人类使用而设计的客户端作为您的界面?您可能应该使用DBI。
-
我相信相当小的 4KiB 在某些系统上是一个“大”量,尽管我似乎记得在我的一台 Linux 机器上具有 128KiB 的管道。
【讨论】:
我过去曾研究过IPC::Run
,但它的lack of support for windows 阻止了我。至于为什么不使用DBI
,我正在尝试运行任意SQL 脚本。一个足够简单的脚本可以 slurped 并在;
上拆分并循环通过,但如果有函数定义或delimiter
切换,或任何数量的其他数据库特定操作,它会中断.. .
恰恰相反,IPC::Run 极端支持Windows。但是没有fork
和没有select
,它的道路上有很多障碍。 (select
仅适用于 Windows 中的套接字。)以上是关于如何在 perl 中使用标量作为 open3 的输入的主要内容,如果未能解决你的问题,请参考以下文章
我们在 C 或 C++ 中是不是有类似于 IPC::Open3 的 perl
perl IPC:Open3 最小通过 perlcritic?