Python/Perlから子プロセスの標準入力にデータを渡す

先日も似た記事を書きました。

Python/Perlからdiffコマンドを起動して2つの文字列の差分を表示

このときは、外部プロセスとの通信に名前付きパイプを使いました。

今回は、子プロセスの標準入力に親プロセスからデータを送ります。名前のないパイプを作成し、パイプの読み取り側を子プロセスの標準入力にし、パイプの書き込み側を親プロセスで保持してデータをパイプに書き込みます。

以下のサンプルコードは、子プロセスにwcコマンドを起動して、文字数を数えています。

(プロセスをforkしたり、パイプをごにょごにょしたりする処理はPerlが書きやすいって思っていたのですが、Pythonでも同じようにできるんですね、という発見で、先日の記事も今回の記事も書きました)

Python

import os
import sys

def exec_wcc(input):
    sys.stdout.flush()
    # execDiff呼び出す前に標準出力のバッファリングが残っていると
    # fork後に重複して出力される

    reader, writer = os.pipe()

    # wcコマンド起動
    pid = os.fork()
    if pid == 0:
        os.close(writer)

        # 標準入力をパイプからの入力で置き換える
        os.dup2(reader, 0) # 0は標準入力の意味

        os.execvp("wc", ["wc", "-c"])

    os.close(reader)

    # パイプにデータを書き込み
    writer = os.fdopen(writer, "w")
    writer.write(input)
    writer.close()

    os.waitpid(pid, 0)

exec_wcc("Hello, world!")
# 13と表示される

Perl

use strict;
use warnings;

# 上記Pythonの例と同様の
# プロセスforkを手動で書く方法
sub exec_wcc_1 {
    my ($input) = @_;

    pipe(PIPE_READER, PIPE_WRITER);

    # wcコマンド起動
    my $pid = fork;
    die $! unless defined($pid);
    if ($pid == 0) {
        close(PIPE_WRITER);

        # 標準入力をパイプからの入力で置き換える
        open(STDIN, "<&=", fileno(PIPE_READER)) or die $!;

        exec("wc", "-c");
    }

    close(PIPE_READER);

    # パイプにデータを書き込み
    print PIPE_WRITER $input;
    close(PIPE_WRITER);

    waitpid($pid, 0);
}

# Perlっぽい簡潔な書き方
sub exec_wcc_2 {
    my ($input) = @_;
    open(WRITER, "|-", "wc -c");
    print WRITER $input;
    close(WRITER);
}

exec_wcc_1("Hello, world!");
# 13と表示される

exec_wcc_2("Hello, world!");
# 13と表示される