2022年2月7日月曜日

PoweShellでパイプがリアルタイムに動作しない?

パイプは,プロセス間通信の一番簡単な手段だと思う. 「|」という記号でコマンドを接続するだけで異なるプログラム通しがデータをやり取りできるようになる. これが便利なのは異なる言語で書かれた既存のプログラムをできるだけ変更せずに接続したい場合に, C言語ならprintf/scanf,Pythonならprint/input文を送信側/受信側に追加するだけでプロセス間通信が実現できる. この便利なパイプがWindows上で妙な挙動をすることを発見したので記録しておく.

実験方法

送信側(sender.py)のプログラムから1秒おきにインクリメントされた数字を標準出力に出力し, 受信側(receiver.py)ではinput関数により受け取った標準入力を即座に標準出力に出す. この2つのプログラムを次のようにパイプでつなぐ.

> python sender.py | python receiver.py

送信側(sender.py)

import sys
import time

for i in range(5):
    print(i, flush=True)
    print("sender: "+ str(i), file=sys.stderr, flush=True)
    time.sleep(1)

受信側(receiver.py)

for i in range(5):
    a = input()
    print("reciever: "+ a)

結果

結果は,どちらもパイプを通してデータの受け渡しが出来ているけど,そのタイミングが全然違う. コマンドプロンプトの場合は送信側が送信し終えたらすぐにパイプを伝って受信側に行き即座に受信側から出力されているけど, PowerShellの場合は,送信側が終了するまで受信側が動作していない.

コマンドプロンプトの場合

sender: 0
reciever: 0
sender: 1
reciever: 1
sender: 2
reciever: 2
sender: 3
reciever: 3
sender: 4
reciever: 4

PoweShellの場合

sender: 0
sender: 1
sender: 2
sender: 3
sender: 4
reciever: 0
reciever: 1
reciever: 2
reciever: 3
reciever: 4

まとめ

要するにPowerShell上だと送信側が終了しないと受信側にデータが受け渡されない. これってコマンドプロンプトからPowerShellになって退化したってことでしょうか? 不具合ではなくそういう仕様?今更PowerShellを勉強する気もないのでそれ以上調べずにここで終わることにした. そういう仕様だとしてもややこしいので同じ記号にするなよって思う. 変なシェルを作らずにUnix系のシェルで統一して欲しい.