Pergunta

I have a number of bash/bind tools that I've written to simplify my command-line existence, and have recently wanted to make one of these tools interactive. If I try to read from stdin in one of these scripts, execution locks up at the point of reading. My example here is in python, but I have seen the exact same behavior when the invoked script is written in ruby:

~> cat tmp.py
import sys
sys.stdout.write(">>>")
sys.stdout.flush()
foo = sys.stdin.readline()
print "foo: %s" % foo,


~> python tmp.py
>>>yodeling yoda
foo: yodeling yoda

So the script works. When I invoke it, I can give it input and it prints what I fed it.

~> bind -x '"\eh":"echo yodeling yoda"'
[output deleted]

~> [Alt-H]
yodeling yoda

bind works as expected. The bound keystroke invokes the command. I use this stuff all the time, but until now, I've only invoked scripts that required no stdin reads.

Let's bind [Alt-H] to the script:

~> bind -x '"\eh":"python tmp.py"'
[output deleted]

Now we're configured to have the script read from stdin while invoked by the bound keystroke. Hitting [Alt-H] starts the script but nothing typed is echoed back. Even hitting [Crl-D] doesn't end it. The only way to get out is to hit [Crl-C], killing the process in the readline. (sys.stdin.read() suffers the same fate.)

~> [Alt-H]
>>>Traceback (most recent call last):
  File "tmp.py", line 7, in <module>
    foo = sys.stdin.readline()
KeyboardInterrupt

As I mentioned at the top, I see the same issue with ruby, so I know it's nothing to do with the language I'm using. (I've omitted the script.)

~> bind -x '"\eh":"ruby tmp.rb"'
[Output deleted]

~> [Alt-H]
>>>tmp.rb:3:in `gets': Interrupt
    from tmp.rb:3

I've looked through the Bash Reference Manual entry on bind, and it says nothing about a restriction on input. Any thoughts?

EDIT:

If I cat /proc/[PID]/fd/0 while the process is stuck, I see the script's input being displayed. (Oddly enough, a fair number of characters - seemingly at random - fail to appear here. This symptom only appears after I've given a few hundred bytes of input.)

Found this, a description of how and when a terminal switches between cooked and raw modes. Calling stty cooked and stty echo at the beginning of prompting, then stty sane or stty raw afterward triggers a new cascade of problems; mostly relating to how bound characters are handled, but suffice it to say that it destroys most alt bindings (and more) until return has been hit a couple times.

Foi útil?

Solução

In the end, the best answer proved to be cooking the tty and manually turning on echo, then reverting the tty settings back to where they were when I started:

def _get_terminal_settings():                                                      
  proc = subprocess.Popen(['/bin/stty', '-g'], stdout=subprocess.PIPE)             
  settings = proc.communicate()[0]                                                 
  os.system('stty cooked echo')                                                    
  return settings                                                                  

def _set_terminal_settings(settings):                                              
  os.system('stty %s' % settings)    

...
...
settings = _get_terminal_settings()
user_input = sys.stdin.readline()
_set_terminal_settings(settings)
...
...

You should be able to do this in any language you choose.

If you're curious about why this insanity is required, I would encourage you to read the link I added (under EDIT), above. The article doesn't cover anywhere enough detail, but you'll at least understand more than I did when I started.

Outras dicas

Hmm, my guess is that what's happening is the python script is running and waiting for input from stdin when you press [Alt-H],but that it's stdin scope is not the same as the stdin scope of the calling script. When you type in something, it goes to the Bash scripts stdin, not the pythons. Perhaps look up a way to "reverse pipe" or forward the stdin from the bash shell to the stdin of a called script?

Edit:

Okay, I researched it a bit, and it looks like pipes might work. Here's a really informative link: bash - redirect specific output from 2nd script back to stdin of 1st program?

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top