forresty
4/1/2018 - 2:19 PM

shell in 100 lines of python

shell in 100 lines of python

#!/usr/bin/env python
import sys
import subprocess, shlex
import os
import readline # for history support

os.environ['PROMPT'] = '> '

def builtin_export(arg):
    key, value = arg.split('=')
    os.environ[key] = value

def builtin_echo(stuffs=''):
    if len(stuffs) > 0 and stuffs[0] == '$':
        print os.environ.get(stuffs[1:]) or ''
    else:
        print stuffs

BUILTINS = {
    'cd': lambda dir='~': os.chdir((os.path.expanduser(dir))),
    'exit': lambda code=0: exit(int(code)),
    'echo': builtin_echo,
    'export': builtin_export
}

def build_commands(words):
    commands = []

    while '|' in words:
        i = words.index('|')
        cmd, args = words[0], words[1:i]
        commands.append((cmd, args))
        words = words[i+1:]

    cmd = words.pop(0)
    commands.append((cmd, words))

    return commands

def get_commands():
    line = raw_input(os.environ['PROMPT'])
    words = shlex.split(line)

    return build_commands(words)

def call_builtin(cmd, args):
    BUILTINS[cmd](*args)

def shell_out(cmd, args, stdin):
    PIPE = subprocess.PIPE
    child = subprocess.Popen([cmd] + args, stdin=PIPE, stdout=PIPE)
    out, _ = child.communicate(stdin)

    return out.strip()

while True:
    try:
        commands = get_commands()
        stdin = None

        for (cmd, args), index in zip(commands, xrange(len(commands))):
            if cmd in BUILTINS:
                call_builtin(cmd, args)
            else:
                if index == len(commands) - 1:
                    # last command in the pipe
                    result = shell_out(cmd, args, stdin)
                    if result:
                        print result
                else:
                    # not last command, set stdin for next command
                    stdin = shell_out(cmd, args, stdin)

    except KeyboardInterrupt:
        print
        pass
    except EOFError:
        print 'exit'
        exit()
    except OSError:
        print 'shell.py: command not found: {0}'.format(cmd)
    except Exception as e:
        print e