Get user input while reading STDIN from a pipe?

2019-05-26 05:49发布

问题:

I'm writing a command line tool (let's call it interactive_rm), that is supposed to read file paths line by line from STDIN so that it can be used with Unix pipes like in the following example:

$ find . | interactive_rm

I'm currently reading each path from STDIN like this:

def _parse_stdin():
    for line in sys.stdin:
        yield prepare_line(line)

Now the problem... Before the tool removes a path it should ask the user for confirmation. To do so I would use input() like this:

for path in _parse_stdin():
    print('Do you want to delete this path: [y/n]\n' + path)
    answer = input()
    if answer == 'y':
        delete(path)

But this doesn't work, because STDIN is already occupied by the pipe, so the input() function is skipped or I end up with an "EOFError: EOF when reading a line".

Does anyone know a solution to this?

回答1:

When stdin is redirected, it needs to reopen the terminal to be able to read from it, e.g.:

from __future__ import print_function

import os

def main():
    tty = os.open("/dev/tty", os.O_RDONLY)
    while True:
        r = os.read(tty, 1024)
        if not r: # Wait for Ctrl-D.
            break
        print("----", r)

if __name__ == "__main__":
    main()

And run it like the following to test it:

python t.py < /dev/null

Note that the terminal may be unavailable. For example, if the command is run through a ssh session without allocating a terminal as ssh -T ... command.