Chapter 16 ■ telnet and SSh
310
for command in 'echo "Hello, world!"', 'uname', 'uptime':
stdin, stdout, stderr = client.exec_command(command)
stdin.close()
print(repr(stdout.read()))
stdout.close()
stderr.close()
client.close()
if name == 'main':
parser = argparse.ArgumentParser(description='Connect over SSH')
parser.add_argument('hostname', help='Remote machine name')
parser.add_argument('username', help='Username on the remote machine')
args = parser.parse_args()
main(args.hostname, args.username)
Unlike all of our earlier Telnet and SSH conversations, this script will receive the output of these three commands
as completely separate streams of data. There is no chance of confusing the output of one of the commands with any
of the others.
$ python3 ssh_commands.py localhost brandon
'Hello, world!\n'
'Linux\n'
'15:29:17 up 5 days, 22:55, 5 users, load average: 0.78, 0.83, 0.71\n'
Besides its security, this is the great advance that SSH provides: the ability to execute semantically separate tasks
on the remote machine without having to make separate connections to the remote machine.
As mentioned in the “Telnet” section earlier, you might find quotes() from the Python pipes module to
be helpful when building command lines for the exec_command() function if you need to quote command-line
arguments so that spaces containing file names and special characters are interpreted correctly by the remote shell.
Every time you start a new SSH shell session with invoke_shell() and every time you kick off a command with
exec_command(), a new SSH “channel” is created behind the scenes to provide the filelike Python objects that let you
talk to the remote command’s standard input, output, and error streams. These channels run in parallel, and SSH will
cleverly interleave their data on your single SSH connection so that all of the conversations happen simultaneously
without ever becoming confused.
Take a look at Listing 16-6 for a simple example of what is possible. Here two command lines are kicked off
remotely, which are each a simple shell script with some echo commands interspersed with sleep pauses. If you want,
you can pretend that these are really filesystem commands that return data as they walk the filesystem or that they
are CPU-intensive operations that only slowly generate and return their results. The difference does not matter at all
to SSH. What matters is that the channels are sitting idle for several seconds at a time and then coming alive again as
more data becomes available.
Listing 16-6. SSH Channels Run in Parallel
#!/usr/bin/env python3
Foundations of Python Network Programming, Third Edition
https://github.com/brandon-rhodes/fopnp/blob/m/py3/chapter16/ssh_threads.py
Running two remote commands simultaneously in different channels
import argparse, paramiko, threading