Paramikoを使ってシェルスクリプトをリモートホストに流しこむ
SSHClient.exec_command
を使って一つずつコマンドを実行することはできるのですが、既存のシェルスクリプトを流しこみたいときどうしたらいいんだろうと試行錯誤しました。
TLDR
明示的にバッファをフラッシュするためにシェルスクリプトの終わりにexit\n
を付ける。
作業ログ
import getpass import paramiko REMOTE_HOST = "YOUR_REMOTE_HOST" c = paramiko.SSHClient() c.set_missing_host_key_policy(paramiko.AutoAddPolicy()) pkey = paramiko.RSAKey.from_private_key_file('/home/ubuntu/.ssh/id_rsa', getpass.getpass()) c.connect(REMOTE_HOST, username='ubuntu', pkey=pkey) # こういう複数行のやつを流しこみたい cmd = ''' /bin/ls / /bin/touch /tmp/foo echo 'foo bar baz' >> /tmp/foo cat /tmp/foo '''
まずbashを起動して標準入力で流しこむ方法を試しました。
stdin, stdout, stderr = c.exec_command('bash -') stdin.write(cmd) print(stdout.read())
しかし応答せず。 どこかでブロックしているのかと考え次にselectの方法を試しました。
import socket import select chan = c.invoke_shell() chan.setblocking(False) chan.send(cmd) while True: r, _, _ = select.select([chan], [], []) if chan in r: try: x = chan.recv(1024) if len(x) == 0: break print(x) except socket.timeout: pass
これでも応答せず。ブロックしているというわけではなさそうなことが分かりました。 じゃあ何だろう、バッファかなと。 ローカルの送信側のバッファは明示的にフラッシュすればいいのですが、リモートホストのバッファをフラッシュする方法が分からず。調べてみたらシェルスクリプトを終了させれば自動的にフラッシュすると記述があったので、スクリプトを修正してみました。
cmd = ''' /bin/ls / /bin/touch /tmp/foo echo 'foo bar baz' >> /tmp/foo cat /tmp/foo exit '''
動いた。なるほど。
まとめ
- 同期非同期の問題ではなかった。
- やりたいことが終わったらプロセスを落とす。シェルかソケットのバッファだろうがプロセス落とすときにはフラッシュされる。