概要
python で sshとかsftp処理しようとすると大抵出て来る paramiko モジュールさん。
超便利なのですが、sshでやり取りする処理を書いて
標準出力を受け取ろうとすると、UnicodeDecodeError が発生するときがあります。
なんでなのかというと、paramiko内部処理の exec_command 関数にて
stdin のみがバイナリモードで、stdoutとstderrがテキストモードで処理しようとしているためです。
たまたま、euc-jpなlinuxマシン用に処理書いててエラーが発生したので知りました。
で、ソースみてみると、モードの部分はパラメータにもなっていないので、ネットで情報探してみると
以下を発見。
Monkey patch for paramiko issue 291 · GitHub
なるほど。置き換えればいいのねってことで、以下自分用の処理つくったのでメモメモ。
サンプル
# coding: utf-8 """ paramiko の SSHClient.exec_command は内部で stdin のみ binary-mode で 処理しているが、stdout と stderr はテキストモードで処理している。 そのため、euc-jp な環境で動かすと UnicodeDecodeError が発生してしまう。 それを防ぐために、stdout, stderr を binary-mode で処理するパッチ関数を以下に定義している。 以下の情報を参考にした。 https://gist.github.com/smurn/4d45a51b3a571fa0d35d """ import paramiko def monkey_patch(): paramiko.SSHClient.exec_command = _patched_exec_command def _patched_exec_command( self, command: str, bufsize: int = -1, timeout: int = None, get_pty: bool = False, environment: dict = None, ) -> tuple: """ 元の exec_command の処理そのままで stdout, stderr を binary-mode で処理します。 """ chan = self._transport.open_session(timeout=timeout) if get_pty: chan.get_pty() chan.settimeout(timeout) if environment: chan.update_environment(environment) chan.exec_command(command) stdin = chan.makefile('wb', bufsize) stdout = chan.makefile('rb', bufsize) stderr = chan.makefile_stderr('rb', bufsize) return stdin, stdout, stderr
あとは、paramiko使う前にmonkey_patchします。
# coding: utf-8 import paramiko import trypython.extlib.paramiko_monkeypatch as paramiko_patch paramiko_patch.monkey_patch() client = paramiko.SSHClient() client.load_system_host_keys() client.connect(hostname='xxx.xxx.xxx.xxx', username='user', password='passwd') sin, sout, serr = client.exec_command('ls -l') for x in sout: print(x.decode('euc-jp'), end='') client.close()
参考情報
Monkey patch for paramiko issue 291 · GitHub
paramiko/client.py at master · paramiko/paramiko · GitHub
Welcome to Paramiko! — Paramiko documentation
過去の記事については、以下のページからご参照下さい。
- いろいろ備忘録日記まとめ
サンプルコードは、以下の場所で公開しています。
- いろいろ備忘録日記サンプルソース置き場