In the previous section we explored start a subprocess and controlling its input and output via pipes. In this section we’ll do the same, but this time for two sub-processes. A use for this, and the original reason I first developed this, was for testing a client and server. Basically, I wanted a program to start up the client and the server, to provide a set of pre-scripted commands to each to get them in a certain state, and then a way of providing my own custom commands, to do some interactive testing.
The Python subprocess module (used for starting subprocesses) is one module that provides scope for heavy usage of pipes. Here we’ll look at this module and how you can use pipes to manipulate the input and output of the spawned subprocess. A Crash Course in the subprocess Module Let’s have a program, for example the Python program detailed below that queries a person for their name and then echos it with a greeting (note this example is a Python program, but we can, in principle, use any program) # say_my_name.py import sys print "what's your name?" for name in iter(sys.stdin.readline, ''): name = name[:-1] if name == "exit": break print "Well how do you do {0}?".format(name) print "what's your name?" This program can be started from within a separate Python process by using the subprocess module, like so: # run_say_my_name.py import subprocess import sys proc = subprocess.Popen(["python", "say_my_name.py"]) while proc.returncode is None: proc.poll() subprocess.Popen creates a Popen object and kicks off a subprocess similar to the one that would be started by typing python say_my_name.py at a command prompt.
In the last section we looked at sending a message through a pipe and everything worked great. However there was a porblem, and this becomes apparent if we alter the sending program and make it run a little slower, perhaps by adding a short sleep in between sending each letter of the message, something like this: import time message = "hello to a pipe\n" with open("my_pipe", "w") as f: print "have opened pipe, commencing writing...." for c in message: f.write(c) print "have sent a letter" time.sleep(1) if we run this program and attempt to read from my_pipe in a different virtual terminal with cat my_pipe we’ll see that we need to show a bit of patience!
In the previous section it was shown how pipes were represented in the operating system in Unix, and how they were written and read from in a way that was very much analagous to how ordinary files are. This is one of the major insights of the Unix family of operating systems, they make explicit the analogies between interprocess communication and ordinary input and output – if a program is made to operate based on the input of plain text and produce plain text as an output it does not matter about the origins of that input, be it a human with a keyboard, another program or even input travelling from across the world over the internet.