Chapter 16 ■ telnet and SSh
294
Quoting Characters for Protection
In the previous section, you used routines in Python’s subprocess module to invoke commands directly. This was
great, and it let you pass characters that would have been special to a normal interactive shell. If you have a big
list of file names with spaces and other special characters in them, it can be wonderful simply to pass them into a
subprocess call and have the command on the receiving end understand you perfectly.
However, when you are using remote-shell protocols over the network, you are generally going to be talking to a
shell like bash instead of getting to invoke commands directly as you do through the subprocess module. This means
that remote-shell protocols will feel more like the system() routine from the os module, which invokes a shell to
interpret your command and therefore involves you in all of the complexities of the Unix command line.
import os
os.system('echo *')
sftp.py shell.py ssh_commands.py ssh_simple.py ssh_simple.txt ssh_threads.py telnet_codes.py
telnet_login.py
The many varieties of system and embedded shells to which your network programs might connect offer all sorts
of quoting and wildcard conventions. They can, in some cases, be quite arcane. Nevertheless, if the other end of a
network connection is a standard Unix shell of the sh family, like bash or zsh, then you are in luck: the fairly obscure
Python pipes module, which is normally used to build complex shell command lines, contains a helper function that
is perfect for escaping arguments. It is called quote, and it can simply be passed a string.
from pipes import quote
print(quote("filename"))
filename
print(quote("file with spaces"))
'file with spaces'
print(quote("file 'single quoted' inside!"))
'file '"'"'single quoted'"'"' inside!'
print(quote("danger!; rm -r "))
'danger!; rm -r '
So, preparing a command line for remote execution can be as simple as running quote()on each argument and
then pasting the result together with spaces.
Note that sending commands to a remote shell with Python does not typically involve you in the terrors of two
levels of shell quoting, which you might have run into if you have ever tried to build a remote SSH command line
that itself uses fancy quoting. The attempt to write shell commands that themselves pass arguments to a remote shell
tends to generate a series of experiments like this:
$ echo $HOST
guinness
$ ssh asaph echo $HOST
guinness
$ ssh asaph echo \$HOST
asaph
$ ssh asaph echo \$HOST
guinness
$ ssh asaph echo \\$HOST
$HOST
$ ssh asaph echo \\$HOST
\guinness