August 1, 2009

Writing a minimal shell in Python

Filed under: Blog — krkhan @ 1:48 am

Being a Unix geek has its own little benefits. For example, when a friend wants you to code a shell for his Operating Systems assignment, you can extort a lunch out of him for the services. Moreover, if you can somehow get him to convince his teacher for accepting submissions in Python, you can have sex with the code itself.

The bare-essentials shell was required to have the following:

  • Support for internal commands such as cd or ls which would be implemented using system calls.
  • Support for launching external programs with arguments provided to the shell — optionally putting them to background with an ampersand at the end of the arguments.

That is it. No fancy intput/output stream redirection, no jobs, no pipes. Just a simple launcher. A quick doze of reflective programming resulted in a class that checked the given commands against member functions (e.g., Shell.on_cd is queried for “cd path“) and called them to process the input. In case of unrecognized commands, the default handler is called which in turn uses the subprocess module and attempts to launch the command as an external program. Support for escaped or quoted arguments wasn’t required (the assignment recommended tokenizing strings at spaces), but the godsent module … :

import shlex
# ...
args = shlex.split(command)

… allowed me to support proper argument parsing with only half a line of code anyway. Similarly, connecting to the standard input/output of the child program wasn’t in the requirements, but the simple call … :

import subprocess
# ...
proc = subprocess.Popen(args,
	executable = args[0],
	stdin = sys.stdin,
	stdout = sys.stdout,
	stderr = sys.stderr)

… did the trick perfectly; allowing proper executions of commands such as “cat -“.

The complete code is downloadable here. Unfortunately, coding was the easy part. Getting the guy to pay for the extorted lunch wasn’t and as of this writing, I’m still deadlocked on that front.

Tags: , , , , , ,