Writing a minimal shell in Python
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 s** with the code itself.
The bare-essentials shell was required to have the following:
- Support for internal commands such as
cd
orls
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: Code, Flag 42, Python, Rants, Shell, shlex, subprocess