Python脚本读取管道stdin后遇EOF无法获取用户输入的解决方法
问题描述
编写了如下Python脚本,功能是选择性读取管道传入的stdin文本并追加到字符串中,随后进入循环接收用户输入,直到读取到'Q'字符时退出。
import sys def read_text_from_stdin(): if sys.stdin.isatty() or not sys.stdin.readable(): return None text = sys.stdin.read().strip() return text if text else None def get_user_input(): print('Write input or [Q]uit: ', end='') try: user_input = input() return user_input except EOFError as e: raise e def main(): stdin = read_text_from_stdin() user_input = f"Test {stdin}" if stdin else 'Test' first_input = True while True: action = user_input if first_input else get_user_input() first_input = False if action == 'Q': sys.exit(0) elif action == '': pass else: print(action) if __name__ == '__main__': main()
直接运行python input_test.py时脚本正常,但通过管道运行(如cat input_test.py | python input_test.py)时,脚本会输出拼接后的字符串,但后续读取用户输入时触发EOFError。推测是首次读取stdin后缓冲区遗留EOF,导致input()调用失败,询问解决办法。
解决办法
问题根源
当通过管道向脚本传入输入时,sys.stdin指向的是管道而非终端。管道内容被sys.stdin.read()读完后,输入流就到达了EOF,后续input()尝试从这个已经耗尽的流读取时,自然会触发EOFError。无法通过写入stdin或清空缓冲区解决这个问题,因为管道的EOF是输入的终点,没有可写入的有效输入源。
可行方案:切换输入源到终端
读取完管道输入后,将sys.stdin重新关联到终端设备,让后续的input()从终端读取用户输入。不同系统的终端设备路径不同:
- Unix-like系统(Linux/macOS):
/dev/tty - Windows系统:
con
修改后的脚本示例(适配Unix-like系统):
import sys def read_text_from_stdin(): if sys.stdin.isatty() or not sys.stdin.readable(): return None text = sys.stdin.read().strip() return text if text else None def get_user_input(): print('Write input or [Q]uit: ', end='') try: return input() except EOFError: print("\n无法获取终端输入,退出") sys.exit(1) def main(): stdin = read_text_from_stdin() # 如果当前stdin不是终端,读完管道后切换到终端输入 if not sys.stdin.isatty(): try: # 重新打开终端设备作为stdin sys.stdin = open('/dev/tty', 'r') except Exception as e: print("无法切换到终端输入,退出") sys.exit(1) user_input = f"Test {stdin}" if stdin else 'Test' first_input = True while True: action = user_input if first_input else get_user_input() first_input = False if action == 'Q': sys.exit(0) elif action == '': pass else: print(action) if __name__ == '__main__': main()
补充说明
- 上述代码通过
sys.stdin.isatty()判断当前输入源是否为终端:如果是管道输入,isatty()会返回False,此时重新打开/dev/tty将输入源切换回终端。 - 若要适配Windows系统,只需将
open('/dev/tty', 'r')替换为open('con', 'r')即可。
内容的提问来源于stack exchange,提问作者Nicholas McCarthy
相关产品推荐
相关产品推荐

