0

I'm trying to write a python application and I need to hide some of the complexity in order to improve the user experience.

To make a long story short, I would like to have something that spawn the default shell selected by the user, reads every user input, and possibly executes some lines of code and changes something in the input before executing the command.

For example, a simple implementation would be:

import os

while 1:

    command = raw_input("$ ")
    words = command.split(' ')

    for i in range(len(words)):

            if  words[i] == 'APPLE':
                    //EXECUTE SOME PYTHON SCRIPT
                    words[i] = 'BANANA'

    newcommand = ' '.join(words)
    os.system(newcommand)

The downside of this implementation is that the shell spawned is not a consistent process, and it doesn't have all the interactive features offered by bash/zsh.

I looked into the subprocess module, and noticed that I can produce an interactive shell with:

import subprocess

sp = subprocess.Popen(["/bin/zsh", "-i"])

sp.communicate()

but I couldn't figure out how to parse the user input and execute something real time. I would much appreciate some help with this.

Thanks in advance for the help.

EDIT: In case I did not make it clear, I'm not interested in parsing the output from the spawned shell, but I'm interested in parsing the user typed input, before that the input is executed.

mrwhite
  • 11
  • 1
  • 1
    do you mean open a new shell and communicate with that? – Padraic Cunningham Jan 01 '15 at 11:57
  • yes, and I need some intermediate layer to parse and potentially change the user input – mrwhite Jan 01 '15 at 12:01
  • This doesn't cover communicating with the process, but it might still be worth reading: http://stackoverflow.com/questions/2804543/read-subprocess-stdout-line-by-line – Aran-Fey Jan 01 '15 at 12:09
  • thanks rawing but 280453 covers processing of output from subprocess, that is well explained in the python documentation. (I'm interested in input processing) – mrwhite Jan 01 '15 at 12:15
  • I think you're trying the impossible. If you want to retain readline functionality of bash (with tab completion etc.) then you have to let bash get the keyboard input, right? But then you'd need a hook into bash to modify the user input, before passing it back to bash for processing. AFAIK bash doesn't offer such hooks, so that this is simply not possible. You'd probably have to make a bash fork and modify the bash sources themselves in order to make this work. – Karel Kubat Jan 01 '15 at 13:10
  • @KarelKubat thanks karel that's the answer that I expected, but I was wondering if there is some way to "intercept" the command when you press the enter key. I suspect there could be some way to do it that does not involve forking bash. – mrwhite Jan 01 '15 at 13:21
  • Use [pexpect](https://pexpect.readthedocs.org/en/latest/) for this, instead of `subprocess`. It's a lot simpler and ought to give you practically everything you want. – Chinmay Kanchi Jan 01 '15 at 15:47
  • @ChinmayKanchi I looked at pexpect but I could not figure out how to do it. Could you give me some more details on how you would proceed in implementing the above please – mrwhite Jan 01 '15 at 16:08
  • Sadly, I'm stuck on Windows at the moment. But basically, you'd send everything to the shell _except_ the `Enter` keypress. When `Enter` is pressed, you'd do your modify the input however you want and then send the modified input + `\n` to the shell. – Chinmay Kanchi Jan 01 '15 at 16:18
  • @ChinmayKanchi: The problem with that is that your program doesn't know what the input line is; it only knows what keystrokes have been sent. (Consider the cases where the user has gotten their shell to do tab-completion or history substitution.) – rici Jan 01 '15 at 16:33
  • @mrwhite: I think python is not the tool you want in this particular problem. If you want to customize bash/zsh behaviour, you'll need to do it in bash/zsh (and be aware that the two are radically different when it comes to hooks). With `zsh`, take a look at the `preexec` hook. With a recent version of `bash`, you could use `bind -x` to bind a shell function to the enter key. – rici Jan 01 '15 at 16:41

1 Answers1

0

You can use python bindings to GNU readline (or libedit) to simulate some of shell's features, like autocompletion, history and quoting.

You can also use other modules like prompt_toolkit that is not simulating existing shells, but provides its own python-based interactive shell.

To use existing shell like bash or zsh, you would need to inject your code into the shell between input parsing and command execution. I think there is no portable way to do it. However, take a look at zsh hook functions.

gavv
  • 4,649
  • 1
  • 23
  • 40