77660


親プロセスが子プロセスを起動し、子プロセスは親プロセスがコマンドを打つたびに結果を返す。  
コマンドと結果のやり取りは標準入出力で一行ずつやり取りする。  
ということを考えています。

次のような実験を行ったのですが、下のようなエラーが出てうまくいきません。どのようにすればよいでしょうか。

メイン:

[code]

    import subprocess
    import sys
    
    with subprocess.Popen(
        [sys.executable, "sub.py"],
        stdin=subprocess.PIPE,
        stdout=subprocess.PIPE,
        encoding="utf-8"
    ) as p:
        print("hoge", file=p.stdin)
        # TODO: 受け取り処理
    
[/code]

sub.py:

[code]

    from sys import stderr, stdin, stdout
    
    for line in stdin:
        # 通る 正常にファイルができる
        with open("out.txt", "a") as f:
            print(line, file=f)
    
        # 通る 正常にコンソールに表示される
        print(line, file=stderr)
    
        # 通らない
        print(line)
    
[/code]

エラー:

[code]

    Exception ignored in: <_io.TextIOWrapper name='<stdout>' mode='w' encoding='cp932'>        
    OSError: [Errno 22] Invalid argument
    
[/code]

環境:  
windows10  
python 3.9.5


CC BY-SA 4.0
2021-06-19T13:06:03.620
77654
2021-06-19T17:34:57.930
20885
question
python
サブプロセスの標準出力を取得したい


`PIPE`を使っていて, 子プロセスから文字列出力しているのに, 親プロセスで受け取っていないから例外が出てるようですね

`# TODO: 受け取り処理` 辺りに処理を追加するとよいかも  
(追記: この処理は, まとめて送信・受信, をおこなうもので「交互にやり取り」は **できません** )

[code]

        stdout = p.communicate()[0]
        print(f'{stdout=}')
        p.stdin.close()
    
[/code]

* * *

あと, `stdin`に `print`するのも 考えてみれば奇妙に映るかもなので, コメントに記す, それっぽいコードにする？など対策あってもよいかも

[code]

        # print 使わないで書き込むとか
        p.stdin.write('hello world\n')
        p.stdin.write('this is a pen\n')
    
[/code]

[code]

        # print 使うにしてもそれっぽいこと示すとか
        from contextlib import redirect_stdout
        with redirect_stdout(p.stdin):
            print('hello world\n')
            print('this is a pen\n')
    
[/code]

* * *

#### (コメントにより)追記

親プロセスと子プロセスで「 **交互** 」にやりとり行うのですね？

その場合, Python同士なら [multiprocessing ---
プロセスベースの並列処理](https://docs.python.org/ja/3/library/multiprocessing.html)
が利用可能でプロセス間通信機能もそれなり豊富です

`subprocess.Popen` 使用する場合, そのままだと標準入力待ちで制御が奪われます(戻ってこない)  
なので, スレッドあるいは(プロセス, あるいは)非同期処理を併用することになるでしょう

  * 非同期処理 (stackoverflow.com) [python asyncio how to read StdIn and write to StdOut?  
](https://stackoverflow.com/questions/64303607/python-asyncio-how-to-read-
stdin-and-write-to-stdout)

* * *

#### さらに追記

generator使う方法があるようです

(元記事からの抜粋)

[code]

    def get_lines():
    
        while True:
            line = proc.stdout.readline()
            if line:
                yield line
    
            if not line and proc.poll() is not None:
                break
    
[/code]

組み合わせが問題ですが, 例えばこんなふうに (記事に合わせ `p` => `proc` として記述)

[code]

        gen = get_lines()
    
        proc.stdin.write('hello world\n')
        proc.stdin.flush()
        print(next(gen))
    
        proc.stdin.write('this is a pen\n')
        proc.stdin.flush()
        print(next(gen))
    
[/code]

参考:
[Pythonのsubprocessで標準出力をリアルタイムに取得する](https://qiita.com/megmogmog1965/items/5f95b35539ed6b3cfa17)


CC BY-SA 4.0
2021-06-19T14:00:07.007
77655
2021-06-19T17:34:57.930
2021-06-19T17:34:57.930
43025
43025
77654
answer


pipe を `flush` するという方法もあるかと思います。

**main.py**

[code]

    import subprocess
    import sys
    
    with subprocess.Popen(
        [sys.executable, "sub.py"],
        stdin=subprocess.PIPE, stdout=subprocess.PIPE,
        encoding="utf-8",
    ) as p:
        for msg in ['hoge', 'fuga', 'piyo']:
            print(msg, file=p.stdin, flush=True)
            line = p.stdout.readline()
            print(line.strip())
    
[/code]

**sub.py**

[code]

    from sys import stderr, stdin, stdout
    
    for line in stdin:
        print(f'OK: {line.strip()}', flush=True)
    
[/code]

**実行**

[code]

    $ python3 --version
    Python 3.9.5
    $ python3 main.py
    OK: hoge
    OK: fuga
    OK: piyo
    
[/code]


CC BY-SA 4.0
2021-06-19T17:32:54.133
77660
2021-06-19T17:32:54.133
77654
answer