09.11.2016 Views

Foundations of Python Network Programming 978-1-4302-3004-5

You also want an ePaper? Increase the reach of your titles

YUMPU automatically turns print PDFs into web optimized ePapers that Google loves.

CHAPTER 16 ■ TELNET AND SSH<br />

You will see that this awkward session bears all <strong>of</strong> the scars <strong>of</strong> a program operating over a terminal.<br />

Instead <strong>of</strong> being able to neatly encapsulate each command and separate its arguments in <strong>Python</strong>, it has<br />

to use spaces and carriage returns and trust the remote shell to divide things back up properly.<br />

■ Note All <strong>of</strong> the commands in this section simply connect to the localhost IP address, 127.0.0.1, and thus<br />

should work fine if you are on a Linux or Mac with an SSH server installed, and you have copied your SSH identity<br />

public key into your authorized-keys file. If, instead, you want to use these scripts to connect to a remote SSH<br />

server, simply change the host given in the connect() call.<br />

Also, if you actually run this command, you will see that the commands you type are actually echoed<br />

to you twice, and that there is no obvious way to separate these command echoes from the actual<br />

command output:<br />

Ubuntu 10.04.1 LTS<br />

Last login: Mon Sep 6 01:10:36 2010 from 127.0.0.9<br />

echo Hello, world<br />

exit<br />

test@guinness:~$ echo Hello, world<br />

Hello, world<br />

test@guinness:~$ exit<br />

logout<br />

Do you see what has happened? Because we did not wait for a shell prompt before issuing our echo<br />

and exit commands (which would have required a loop doing repeated read() calls), our command text<br />

made it to the remote host while it was still in the middle <strong>of</strong> issuing its welcome messages. Because the<br />

Unix terminal is by default in a “cooked” state, where it echoes the user's keystrokes, the commands got<br />

printed back to us, just beneath the “Last login” line.<br />

Then the actual bash shell started up, set the terminal to “raw” mode because it likes to <strong>of</strong>fer its own<br />

command-line editing interface, and then started reading your commands character by character. And,<br />

because it assumes that you want to see what you are typing (even though you are actually finished<br />

typing and it is just reading the characters from a buffer that is several milliseconds old), it echoes each<br />

command back to the screen a second time.<br />

And, <strong>of</strong> course, without a good bit <strong>of</strong> parsing and intelligence, we would have a hard time writing a<br />

<strong>Python</strong> routine that could pick out the actual command output—the words Hello, world—from the rest<br />

<strong>of</strong> the output we are receiving back over the SSH connection.<br />

Because <strong>of</strong> all <strong>of</strong> these quirky, terminal-dependent behaviors, you should generally avoid ever using<br />

invoke_shell() unless you are actually writing an interactive terminal program where you let a live user<br />

type commands.<br />

A much better option for running remote commands is to use exec_command(), which, instead <strong>of</strong><br />

starting up a whole shell session, just runs a single command, giving you control <strong>of</strong> its standard input,<br />

output, and error streams just as though you had run it using the subprocess module in the Standard<br />

Library. A script demonstrating its use is shown in Listing 16–6. The difference between exec_command()<br />

and a local subprocess (besides, <strong>of</strong> course, the fact that the command runs over on the remote machine!)<br />

is that you do not get the chance to pass command-line arguments as separate strings; instead, you have<br />

to pass a whole command line for interpretation by the shell on the remote end.<br />

284

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!