logo
Published on

Pythonのpexpectでexpect的な対話シェル自動化

Authors

LinuxのBashシェルには、expectというビルトインのコマンドがあります。これは例えばパスワード入力されるプロンプトなどのインタラクティブシェルを自動で入力し、コマンドライン作業のオートメーション化を図るのに利用されます。 このexpectのPythonワッパー的なものとして、pexpectパッケージがあります。

以下でpexpectをインストールします。

pip install pexpect

pexpectの使用方法の例として、以下にGithubへのpushまでのステップとpexpectを利用してのパスワード入力のプロンプトの自動入力までを示します。

まず、git add .などの単純なコマンドをpythonで実行するには、以下のように行います。 以下はPython 3.6.7での例です。pythonのバージョンが異なる場合はshell=Trueあたりの非互換性がエラーを起こす可能性があります。 pythonを複数バージョンインストールし、使い分けを行うにはpyenvなどのバージョン管理システムを使用することを推奨します。pyenvについては他の記事で解説しています。当ブログの検索機能を利用してください。

# Python 3.6.7

import pexpect
import subprocess
import os

cmd = subprocess.run("git add .", shell=True, stderr=subprocess.PIPE, stdout=subprocess.PIPE)
if ( cmd.stdout ) : print( cmd.stdout.decode('utf-8') )
if ( cmd.stderr ) : print( cmd.stderr.decode('utf-8') )

続けて、gitのcommitまでを行います。

cmd = subprocess.run("git status", shell=True, stderr=subprocess.PIPE, stdout=subprocess.PIPE)
if ( cmd.stdout ) : print( cmd.stdout.decode('utf-8') )
if ( cmd.stderr ) : print( cmd.stderr.decode('utf-8') )

cmd = subprocess.run("git commit -m .", shell=True, stderr=subprocess.PIPE, stdout=subprocess.PIPE)
if ( cmd.stdout ) : print( cmd.stdout.decode('utf-8') )
if ( cmd.stderr ) : print( cmd.stderr.decode('utf-8') )

以下が具体的なpexpectの使用例です。ユーザー名のプロンプトとパスワードのプロンプトを自動入力します。 ユーザー名とパスワードの環境変数をそれぞれセットしておく必要があります。

child = pexpect.spawn('git push -u origin gh-pages')
child.expect('Username .*')
child.sendline(os.environ['GITHUB_USERNAME'])
child.expect('Password .*')
child.sendline(os.environ['GITHUB_PASSWORD'])
child.expect(pexpect.EOF, timeout=None)
cmd_show_data = child.before
print( cmd_show_data.decode() )

全体のスクリプトとしては、以下のようになります。

# Python 3.6.7

import pexpect
import subprocess
import os

cmd = subprocess.run("git add .", shell=True, stderr=subprocess.PIPE, stdout=subprocess.PIPE)
if ( cmd.stdout ) : print( cmd.stdout.decode('utf-8') )
if ( cmd.stderr ) : print( cmd.stderr.decode('utf-8') )

cmd = subprocess.run("git status", shell=True, stderr=subprocess.PIPE, stdout=subprocess.PIPE)
if ( cmd.stdout ) : print( cmd.stdout.decode('utf-8') )
if ( cmd.stderr ) : print( cmd.stderr.decode('utf-8') )

cmd = subprocess.run("git commit -m .", shell=True, stderr=subprocess.PIPE, stdout=subprocess.PIPE)
if ( cmd.stdout ) : print( cmd.stdout.decode('utf-8') )
if ( cmd.stderr ) : print( cmd.stderr.decode('utf-8') )

child = pexpect.spawn('git push -u origin gh-pages')
child.expect('Username .*')
child.sendline(os.environ['GITHUB_USERNAME'])
child.expect('Password .*')
child.sendline(os.environ['GITHUB_PASSWORD'])
child.expect(pexpect.EOF, timeout=None)
cmd_show_data = child.before
print( cmd_show_data.decode() )