I want to write a Python script that runs another program, reading the output of the other program and manipulating it. The problem is that this program prompts for a password, and I cannot figure out how to supply it automatically. (For the purposes of this script, it really does not matter if the password is stored in plain-text in the script itself.) What I want to do is something like:
os.system('echo someinput | /var/local/bin/someprogram')
Which results in someprogram giving me the unwanted password prompt, and also doesn't give me the program's output as the return value. Tragically, the program does not have a way to bypass this prompt.
Unfortunately, I also have some restrictions as to how I can go about solving this problem. First, I'm stuck with Python 2.3 (so I cannot use the subprocess module). Second, I cannot install any new modules, (so no pexpect). Fortunately, it doesn't have to be particularly portable, so a Linux-only solution is fine.
I've been trying to figure out the pty module, since it looks like it offers what I need, but after spending hours wrestling with it, I just cannot figure out how to get it to work the way I need it to.
解决方案
I had some similar problems with terminal-based interprocess communication that didn't seem to be solvable using popen (et al.). I ended up learning how to use pty by reading the source of pexpect, which contains examples of how (and comments of why) to get pty to jump through the necessary hoops.
Depending on your needs, of course, you could also just use pexpect!
Here's the meat of what I used in my own project. Note that I'm not checking to see whether the child process terminates; the script was intended to run as a daemon managing a long-running Java process, so I never had to deal with status codes. Hopefully, this will get you most of what you need, however.
import os
import pty
import select
import termios
child_pid, child_fd = pty.fork()
if not child_pid: # child process
os.execv("/path/to/command", ["command", "arg1", "arg2"])
# disable echo
attr = termios.tcgetattr(child_fd)
attr[3] = attr[3] & ~termios.ECHO
termios.tcsetattr(child_fd, termios.TCSANOW, attr)
while True:
# check whether child terminal has output to read
ready, _, _ = select.select([child_fd], [], [])
if child_fd in ready:
output = []
try:
while True:
s = os.read(child_fd, 1)
# EOF or EOL
if not s or s == "\n":
break
# don't store carriage returns (no universal line endings)
if not s == "\r":
output.append(s)
except OSError: # this signals EOF on some platforms
pass
if output.find("Enter password:") > -1:
os.write(child_fd, "password")