pexpectとは
https://pexpect.readthedocs.io/en/stable/
対話式のコマンドを自動化するために使われる、expectというコマンドのPython版です。
expectは昔からある有名なコマンドぽいですが、tclという言語を使わなければならず大変とてもつらいので、python実装をできれば使いたい。そんなとこです。
pexpectの問題
しかしここで問題になるのがpythonなこと。
そもそもすべてのコマンドが対話なわけでなく、基本的には普通にコマンドを並べて、たまにpexpectしたい場合、そこだけpythonの別ファイルにしなければならず、別ファイルになって見通しが悪くなったり、引数を渡してあげたりしなきゃいけません。
pexpect+xonsh
xonshだと下みたいにかけます。便利。
(適当な例なのでchpasswdは投げつけないでください…)
ということでpexpect使う場合はxonsh使うと便利と言う話でした。
ちなみにpexpectの部分だけコンソール上に表示されないのは気持ち悪いので、logfile=os.fdopen(sys.stdout.fileno(), 'wb')
で端末上に出るようにしています。
subprocessはプログレスバーみたいなものが来ると表示が崩れたりして微妙という私見です。
import pexpect
import os, sys
username = "test"
passwd = "hoge"
sudo useradd @(username)
p = pexpect.spawn("sudo passwd "+username, logfile=os.fdopen(sys.stdout.fileno(), 'wb'))
p.expect("password:")
p.send(passwd+"\n")
p.expect("password:")
p.send(passwd+"\n")
mkdir @(username)
# chown...
シリアルコンソールの自動化をする
多分こっから先は、タイトルの機能を切望してる人意外は読まなくていいと思います。
上記の話は正直、そもそも対話を自動化したい時とかあまり無いし、だいたいオプションでなんとかなる。みたいなとこあると思います。長時間ターミナルで対話してるとしても、それ全部シェルスクリプトで自動化できるからみたいなことあると思います。
しかし、ブートローダやRTOSなどでスクリプトというものがない場合や、そもそもネットワークが物理的に使えない場合、ネットワークドライバがまだ安定してない場合、シリアルコンソールを自動化したいことが多々あるんです。そういう話です。
fdpexpect
先ほどのpexpectに、コマンドのspawnではなくてファイルデバイスを操作するfdpexpectというものがあります。
例えば
- /dev/ttyUSB0にボーレート38400で接続
- ローダーのオートブートを止めて適当な設定
- 成功したらブート
- Linux立ち上がったらsshで接続
を書くとこんな感じになります。
import serial
from pexpect import fdpexpect
# 1.
ser = serial.Serial('/dev/ttyUSB0', 38400)
terminal = fdpexpect.fdspawn(ser, logfile=os.fdopen(sys.stdout.fileno(), 'wb'), timeout=5)
# 2.
terminal.expect("AutoBoot...")
terminal.send("\n")
terminal.expect("loader#")
terminal.send("some boot loader command")
# 3.
res = terminal.expect(["loader#", "Error"])
if res:
error_handle()
else:
terminal.send("boot command")
# 4.
terminal.expect("root#")
ssh user@address
fdpexpect+xonshでできること
上の例はそのままsshしてるだけ(でも便利)ですが、
- カーネルがエラー吐いたのをトリガにgrepしてエラーメッセージのコードをvimで立ち上げる
- 対向をホストPCにしたネットワークドライバのテスト自動化
- 起動オプションとftpサーバで見えるカーネルを同期取りながらいろいろなブート試す
とかそんな感じのことできたりします。
…以上です。