pexpect において、標準出力にログを出力させながら、端末の出力に関係なくコマンドを実行させたいとき
この記事を作った動機 Onenote代替品の作成の研究の一環で、pexpect を使って端末のセッションを維持させながらとにかくコマンドを実行させたいということがあり、その過程で試行錯誤があったため記録したい。 現状使っているもの 以下は現状使っている実装の参考例である。一定時間置いたあとに、何でもマッチングするexpectを配置しているのは、標準出力であるstdoutに対して、コマンドの実行結果を吐き出させたいためである。作ったセッション内のターミナルに何が出力されるかわからないが、とにかくコマンドを実行させて、その結果を出力することに以下のスクリプトは特化している。 待ち時間は適宜調整する必要がある。 スクリプト本体 import pexpect,time,sys session = pexpect.spawn("/bin/bash",timeout=5, encoding='utf-8') session.logfile_read = sys.stdout session.sendline("ls") time.sleep(1) session.expect(".+") session.sendline("gcc") time.sleep(1) session.expect(".+") 実行結果 python main1.py ls # ⠀aaaa ⠀main.py ⠀main1.py ⠀readme.md ⠀test/ gcc # gcc: fatal error: 入力ファイルがありません # compilation terminated. 追記 (2026/9/16) このやり方は非常に簡易的にしか動作せず、副作用があることが分かった。具体的には".+“でexpectさせるとログが出てくるのは、pexpectが期待した文字列を待機している状態になるからであることが分かった。つまるところ、タイムアウトに引っかかると例外を出して強制終了してしまう。 それを防ぐには、pexpectにおいては、waitという専用の関数があるため、それを利用することができることが分かった。ただこれだとログが出てこない(”.*“と同じ挙動になる)ため、なにかさらに工夫が必要そうなことが分かっている。 また、pexpect にて同じ端末に出力を出させると端末の表示がぐちゃぐちゃになってしまう。最近考えていることとして個別にファイルとしてlogをテキストファイルとして書き出させてそれをwatch -n 0.1 "cat log | tail -n 10"みたいにすればよいのではないかと思い始めている。 うまくいかなかったこと 単にsendlineする 単にsendlineすると、コマンド自体は実行されるが、一切標準出力にログが出力されないことになる。 スクリプト本体 import pexpect import time,sys session = pexpect.spawn("/bin/bash",timeout=5, encoding='utf-8') session.logfile_read = sys.stdout session.sendline("ls") time.sleep(1) session.sendline("echo test > test1") time.sleep(1) 実行結果 python main2.py # 何も出てこない cat test1 # test エンコードを指定しない 端末のセッションを作るときに、文字コードを明示しなかった場合、そもそもスクリプトを実行できないことがある。 ...