Skip to content

Instantly share code, notes, and snippets.

@adisbladis
Created June 8, 2018 02:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save adisbladis/d294be83f8035015b5421fff179e6665 to your computer and use it in GitHub Desktop.
Save adisbladis/d294be83f8035015b5421fff179e6665 to your computer and use it in GitHub Desktop.
Paramiko example with proper stderr/stdout behaviour
import contextlib
import functools
import paramiko
import select
import sys
import os
if __name__ == '__main__':
host = '172.28.10.2'
username = 'adisbladis'
shell_script = f'''
set -euxo pipefail
echo COOL
'''
client = paramiko.SSHClient()
# Contextlib can be "abused" to implement
# defer-like behaviour
with contextlib.ExitStack() as stack:
def defer(fn, *args, **kwargs):
partial = functools.partial(fn, *args, **kwargs)
stack.callback(partial)
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
client.connect(host, username=username)
defer(client.close)
session = client.get_transport().open_session()
forward = paramiko.agent.AgentRequestHandler(session)
stdin, stdout, stderr = client.exec_command('exec bash')
defer(stdin.close)
defer(stdout.close)
defer(stderr.close)
stdin.write(shell_script)
stdin.close()
# Stdout/stderr are multiplexed over the same channel
chan = stdout.channel
# Close channel in outgoing direction
# Cannot write any more data after this
chan.shutdown_write()
# Flush fds before exit
defer(sys.stdout.buffer.flush)
defer(sys.stderr.buffer.flush)
# Emulate select() over stdout/stderr
read_size = 1024
while not chan.exit_status_ready():
if chan.recv_ready():
b = chan.recv(read_size)
while b:
sys.stdout.buffer.write(b)
b = chan.recv(read_size)
if chan.recv_stderr_ready():
b = chan.recv_stderr(read_size)
while b:
sys.stderr.buffer.write(b)
b = chan.recv_stderr(read_size)
exit(chan.recv_exit_status())
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment