8
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Windowsでpexpectを利用する

Posted at

経緯

以前、pexpectでWindowsのコマンドの自動化みたいなのをやろうとしてpexpectのspawnが使えないことが分かり、その流れからPexpectが使えないと思い込み、結局Linuxに切り替えて開発した経験があった。 その後はLinux中心で開発していたため知らなかったのだが、Windowsでpexpectが使用できるとのことだったので、以前を振り返りながら実際にWindowsでpexpectを利用してみた。

実行環境

OS:Windows10
Python Version: 3.6.1
Pexpect Version: 4.2.1

前提

なぜspawnが利用できなかったのか

そもそもspawnが利用できなかった理由は2つある。 1つはimport時の問題で、もう一つはspawnそのものにあった。

import時の問題

私がpexpectを使用する際に書いていたコードは以下になる。
python
import pexpect
process = pexpect.spawn("cmd")

上記ではimportはできるものの、spawnを使用できない(has no Attributeエラーが出る。)
その原因はpexpectというmoduleの__init__.pyファイルにある。
spawnはclassであり、pexpectというpackageの中のpty_spawn.py内にある。
このpty_spawnはpexpectの__init__.pyの中でsys.platform != 'win32'の時のみimportされるように書かれている。
よってWindowsではimport pexpectではspawnが利用はできないのである。

spawnの問題?

では、なぜそのようなimport方法になっているか? それはpty_spawnが名前の通りpty(擬似端末)を使用するplatformでの使用を前提に作られたものだからである。(そもそもpexpectがその前提で作られていたのかもしれない) Windowsではptyを使用することはできない。そのためpty_spawnをimportしようとするとExceptionを吐く。興味がある方はやってみてほしい。

解決策

Windowsでpexpectを使用するには、pexpectのpackageの中のpopen_spawn.pyにあるPopenSpawnクラスを使用するとのこと。 pexpectのdocument[^1]に記載があるが、このクラスはpexpectのversion4.0から追加になったwindows向けのクラスとのこと。 documentのhistory等このクラスやversion周辺を読むと、既存のspawnクラスやクロスプラットフォームについても少し触れられているので、一度読んでみても良いと思われる。

作成プログラム

PopenSpawnクラスを使用したコードを作成したため載せておく。 ただし、コマンドプロンプトのコマンドを実行させて結果を取得するという用件のみを満たしたコードなので、さまざまな考慮抜けがあると思われるが、そこはご了承いただきたい。 動作確認は行っており、コマンドの実行ができていることは確認済みである。
test.py
import pexpect.popen_spawn as psp

class WinPexpect():
    def __init__(self):
        self.process = psp.PopenSpawn("cmd")
        self.prompt = ">"
        try:
            self.process.expect(">", timeout=10)
        except:
            Exception("cmd expect fail")
        print(self.cmd_readlines())

    def cmd_readlines(self):
        res = ""
        res = self.process.before.decode("shift-jis", errors="ignore") + self.process.after.decode("shift-jis", errors="ignore")
        self.prompt = res.splitlines()[-1]
        self.prompt = self.prompt.replace("\\", "\\\\")
        return res

    def cmd_sendline(self, cmd, timeout=10):
        self.process.sendline(cmd)
        if cmd.find("cd ") > -1:
            self.prompt = self.prompt[:-1] + "\\\\" + cmd.split("cd ")[1] + ">"
        try:
            self.process.expect(self.prompt, timeout=timeout)
            return True
        except:
            import traceback
            traceback.print_exc()
            return False

if __name__ == '__main__':
    wincmd = WinPexpect()
    wincmd.cmd_sendline("dir")
    print(wincmd.cmd_readlines())
    wincmd.cmd_sendline(" cd log")
    print(wincmd.cmd_readlines())

反省点

windowsでpexpectが使えないと判断したのは、ほぼほぼ私がしっかりソースコードやdocumentを読まなかったことに原因があるといってもいい。 これに関しては今後も似たような判断をしないとは言えないため、細心の注意を払ってプログラミングを行っていきたい。
8
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?