Foundations of Python Network Programming

(WallPaper) #1
Chapter 16 ■ telnet and SSh

309

Also, if you run this command, you will see that the commands you type are actually echoed to you twice and that
there is no obvious way to separate these command echoes from the actual command output.


Welcome to Ubuntu 13.10 (GNU/Linux 3.11.0-19-generic x86_64)
Last login: Wed Apr 23 15:06:03 2014 from localhost


echo Hello, world
exit
test@guinness:~$ echo Hello, world
Hello, world
test@guinness:~$ exit
logout


Can you guess what has happened?
Because you did not pause and wait patiently for a shell prompt before issuing the echo and exit commands
(which would have required a loop doing repeated read() calls), the command text was delivered to the remote host
while it was still in the middle of issuing its welcome messages. Because the Unix terminal is in a “cooked” state by
default where it echoes the user’s keystrokes, the commands got printed, just beneath the “Last login” line.
Then the actual bash shell started up, set the terminal to raw mode because it likes to offer its own command-line
editing interface, and then started reading the commands character by character. Because it assumes you want to see
what you are typing (even though you actually finished typing and it is just reading the characters from a buffer that is
several milliseconds old), it echoes each command back to the screen a second time.
Of course, without a good bit of parsing and intelligence, you would have a hard time writing a Python routine
that could pick out the actual command output (the words Hello, world) from the rest of the output you are
receiving over the SSH connection.
Because of all of these quirky, terminal-dependent behaviors, you should generally avoid ever using invoke_shell()
unless you are actually writing an interactive terminal program where you let a live user type commands.
A much better option for running remote commands is to use exec_command() that, instead of starting up a
whole shell session, just runs a single command. It gives you control of that command’s standard input, output, and
error streams just as though you had run that command locally using the subprocess module in the Standard Library.
Listing 16-5 shows a script demonstrating its use. The difference between exec_command() and a local subprocess
(besides, of course, the fact that the command runs over on the remote machine!) is that you do not get the chance to
pass command-line arguments to the remote server as separate strings. Instead, you have to pass a whole command
line for interpretation by the shell on the remote end.


Listing 16-5. Running Individual SSH Commands


#!/usr/bin/env python3


Foundations of Python Network Programming, Third Edition


https://github.com/brandon-rhodes/fopnp/blob/m/py3/chapter16/ssh_commands.py


Running three separate commands, and reading three separate outputs


import argparse, paramiko


class AllowAnythingPolicy(paramiko.MissingHostKeyPolicy):
def missing_host_key(self, client, hostname, key):
return


def main(hostname, username):
client = paramiko.SSHClient()
client.set_missing_host_key_policy(AllowAnythingPolicy())
client.connect(hostname, username=username) # password='')

Free download pdf