2

入力として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}} を直接使用しない理由は、このコードが実際にはフットプリントのない自動化に使用するより大きなコード本体の一部だからです。つまり、管理者が特別なツールをインストールしていない (RHEL 5/6 に標準装備されているものだけ) 30 台以上のサーバーの環境があります。これらのサーバーは、各環境 (local、dev、qa、beta、prod)、各プロジェクト (...) ごとに、一連のサーバー (db、app、web) に分割されます。とにかく、非常に一般的なタスクの 1 つは、データベースをある場所から別の場所にコピーすることです。次のようなコマンドでそれを実現します。

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"

これは、本番データベースを開発環境にクローンする最も基本的なバージョンです (より典型的なバージョンには、各コマンドに多数のスイッチが含まれ、途中で多数のパイプ コマンドが含まれます)。そこで、これに関する抽象化のライブラリを作成しました (IPC::Open3::Callback::*)。途中で、データベースのコピー後に実行する必要があるいくつかの SQL コマンドを実行する必要がありました。そのため、任意の SQL スクリプト セットを実行する機能を追加しました (クローン操作のソースと宛先に基づいて)。次のようなコマンドでそれらを実行できます。

$command_runner->run_or_die( pipe_command(
    "cat $post_restore",
    command( "mysql dbname", $dest_config ) ) );

しかし、SQL スクリプトのコンテンツの一部を変更する必要があることに気付いたので、それを丸呑みして少し作業を行い、それを$command_runnerasに提供したいと思いました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、スカラー参照の場合は機能しません。とにかく、それは長い話です。

4

1 に答える 1

2

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);

しかし、それは危険です。デッドロックの危険があります。(大量の[1]を STDIN に送信しようとしているときに、子が大量の[1]を STDOUT または STDERR に送信しようとすると、デッドロックが発生します。) この問題を回避するには、ループが必要です。これは非常に難しいです。この低レベルのものは使いたくありません。IPC::Run3またはIPC::Runを使用してください。それらはすべての面倒な作業を代わりに行います。selectopen3

use IPC::Run3 qw( run3 );
run3($shell_cmd, \$sql, \my $out, \my $err);

いっそのこと、不必要なシェルは避けてください:

run3([ $prog, @args ], \$sql, \my $out, \my $err);

しかし、なぜ人間が使用するように設計されたクライアントをインターフェイスとして使用しているのですか? おそらくDBIを使用しているはずです。


  1. 私のLinuxマシンの1つで128KiBのパイプを覚えているようですが、かなり小さい4KiBは一部のシステムでは「大量」であると思います。
于 2015-10-19T03:26:18.213 に答える