子プロセスに標準入力と標準出力を中継

下のようなコードを書いてsedでテストをしていたのだけどうまく動かない。

import sys
from subprocess import *
from time import sleep

cmd = sys.argv[1:]
bufsize = 1
p = Popen(cmd, shell=True, stdout=PIPE, stdin=PIPE, bufsize=bufsize)

while True:
    sleep(0.1)
    print 1
    data = sys.stdin.readline()
    print 2, repr(data)
    p.stdin.write(data)
    print 3
    data = p.stdout.readline()
    print 4
    sys.stdout.write(data)

こうなってしまう。

$ python ~/tmp/watch_stdin.py "sed s/a/b/g"
1
aaa
2 'aaa\n'
3

本当は子プロセスの標準入力にaaa\nが入ると子プロセスが標準出力にbbbを返すはず。

$ sed s/a/b/g
aaa
bbb

という悩みをfrsyuki君に相談したらRubyで書いてみたけどやはりsedではうまく行かなくて、catならうまく行くという話。

$ python ~/tmp/watch_stdin.py "cat"
1
aaa
2 'aaa\n'
3
4
aaa
1

ほんとだ、うまく動く。「これはもうsedの実装を読むしかありませんね」(上野氏)


とりあえずきれいにしておいたバージョンを載せる。

import sys
from subprocess import Popen, PIPE
from time import sleep

cmd = sys.argv[1:]
bufsize = 1
p = Popen(cmd, shell=True, stdout=PIPE, stdin=PIPE, bufsize=bufsize)

while True:
    sleep(0.1)
    data = sys.stdin.readline()
    if not data: break
    p.stdin.write(data)
    data = p.stdout.readline()
    sys.stdout.write(data)