Python CLI 脚本中的管道和提示

5 投票
3 回答
2241 浏览
提问于 2025-04-16 19:21

在Python的命令行脚本中,能不能把管道输入和TTY提示结合起来?比如,运行下面这个:

import sys

piped_text = None

if not sys.stdin.isatty():
    piped_text = sys.stdin.read()

user_in = raw_input('Enter something: ')

if piped_text:
    sys.stdout.write(piped_text)

sys.stdout.write(user_in + '\n')  

会产生这样的输出:

~: python mrprompt.py
Enter something: something
something
~: echo foo | python mrprompt.py
Enter something: Traceback (most recent call last):
  File "mrprompt.py", line 9, in <module>
    user_in = raw_input('Enter something: ')
EOFError: EOF when reading a line

但我想要的输出是这样的:

~: python mrprompt.py
Enter something: something
something
~: echo foo | python mrprompt.py
Enter something: something
foo
something

换句话说,子进程能不能知道它父进程的TTY?在Python中,能不能和父进程的TTY进行交互?我在GNU Screen里使用bash(所以,读取'SSH_TTY'这个环境变量就不太适用了)。

3 个回答

1

在同一个程序中,可以同时接受管道输入和用户输入。

处理这个问题最简单的方法是使用 subprocess 模块。你可以在你的 Python 程序中执行其他程序。这个模块的参数可以让你设置与正在执行的程序之间的管道。

这很简单。这是标准做法。效果很好。

同样,通过程序参数接受文件也是如此。这也很简单。这是标准做法。效果很好。额外的好处是,在 Windows 的 CMD.EXE/COMMAND.COM 中,管道会自动使用临时文件。由于 DOS/Windows 不支持真正的管道,所以做更复杂的事情没有任何好处。

我知道命令行的 mysql 客户端代码在处理管道输入之前,会先接受用户输入的密码。Linux(可能还有 Mac OS X)上的解决方案可能和 Nemo 提到的类似。如果你真的想要这样的行为,并且不在乎 Windows,那就可以使用这个解决方案。(在 Windows 上,你需要使用像 wconio 这样的包。-- http://newcenturycomputers.net/projects/wconio.html

确实可以让用户输入并接受管道输出,输出可以发送到另一个程序。这里有三个标准的文件句柄:stdin(标准输入)、stdout(标准输出)和 stderr(标准错误)。即使 stdout 被重定向到文件,你也可以写入 stderr。

5

你可以检查你的 stdin 输入是不是来自管道,就像使用 sys.stdin.isatty 一样。你会遇到 EOF(文件结束符),是因为你用 stdin.read() 读取了所有的输入,然后又想用 raw_input() 再读取一些。

你不能同时从管道输入和进行交互式输入。如果你在用管道输入数据,那么就没有其他的 stdinraw_input 使用。此时,唯一的 stdin 就是来自文件的那一份。

你需要做的是提供一个可选的方式,让你的脚本的某个部分可以从文件中读取输入,像这样设置一个开关:

--input=file.txt

然后为其他部分提供交互式的提示。

7

这个代码大致上是可以工作的:

#!/usr/bin/python

import sys

saved_stdin = sys.stdin
sys.stdin = open('/dev/tty', 'r')
result = raw_input('Enter something: ')
sys.stdout.write('Got: ' + result + '\n')
sys.stdin = saved_stdin
result2 = sys.stdin.read()
sys.stdout.write('Also got: ' + result2)

把它保存为 foo.py,然后试试输入 echo goodbye | ./foo.py

当然,/dev/tty 这个东西只在 Unix 系统上存在。如果你在一个没有控制终端的进程中运行它,open() 这个函数可能会失败……

撰写回答