Python中读取stdin后如何正常使用input()?
NetCat脚本中input()异常的原因解析
问题背景
下面是一段NetCat工具的Python脚本:
#!/usr/bin/env python3 import argparse import socket import shlex import subprocess import sys import textwrap import threading def execute(cmd): cmd = cmd.strip() if not cmd: return output = subprocess.check_output(shlex.split(cmd), stderr=subprocess.STDOUT) return output.decode() class NetCat: def __init__(self, args, buffer=None): self.args = args self.buffer = buffer self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) def run(self): if self.args.listen: self.listen() else: self.send() def send(self): self.socket.connect((self.args.target, self.args.port)) if self.buffer: self.socket.send(self.buffer) try: while True: recv_len = 1 response = '' while recv_len: data = self.socket.recv(4096) recv_len = len(data) response += data.decode() if recv_len < 4096: break if response: print(response) buffer = input('> ') #input does not return after new line or even EOF (ctrl-d) buffer += '\n' self.socket.send(buffer.encode()) except KeyboardInterrupt: print('User terminated.') self.socket.close() sys.exit() def listen(self): pass if __name__ == '__main__': parser = argparse.ArgumentParser( description='Net Tool', formatter_class=argparse.RawDescriptionHelpFormatter, epilog=textwrap.dedent( '''Examples: netcat.py -t 192.168.1.108 -p 5555 -l -c # command shell netcat.py -t 192.168.1.108 -p 5555 -l -u=mytest.txt # upload to file netcat.py -t 192.168.1.108 -p 5555 -l -e=\"cat /etc/passwd\" # execute command echo 'ABC' | ./netcat.py -t 192.168.1.108 -p 135 # echo text to server port 135 netcat.py -t 192.168.1.108 -p 5555 # connect to server ''' ) ) parser.add_argument('-c', '--command', action='store_true', help='command shell') parser.add_argument('-e', '--execute', help='execute specified command') parser.add_argument('-l', '--listen', action='store_true', help='listen') parser.add_argument('-p', '--port', type=int, default=5555, help='specified port') parser.add_argument('-t', '--target', default='127.0.0.1', help='specified IP') parser.add_argument('-u', '--upload', help='upload file') args = parser.parse_args() if args.listen: buffer = '' else: buffer = sys.stdin.read() # stdin is used here, but should that change behaviour of subsequence reading - input() ? nc = NetCat(args, buffer.encode()) nc.run()
执行脚本时遇到以下异常:
- 执行
input('> ')后,无论输入换行还是EOF(Ctrl+D),语句均无法返回; - 通过管道重定向stdin(如
$ echo 'ABC' | python3 test.py -p 9998)时,input()直接抛出EOFError: EOF when reading a line,无提示符出现。
触发场景:
- 无输入触发:执行
$ python3 test.py -p 9998,发送消息后出现'> '提示符,但输入新消息后无响应; - 重定向stdin触发:发送ABC后直接抛出EOFError,无提示符。
核心原因:stdin流被sys.stdin.read()耗尽
sys.stdin.read()的行为是一次性读取整个标准输入流的所有内容,直到遇到EOF(文件结束符)。一旦执行这个操作,stdin流的读取指针会移动到流的末尾,后续对该流的读取操作(包括input())会面临两种情况:
1. 重定向stdin场景(如管道输入)
当用echo 'ABC' | python3 test.py -p 9998时,echo输出的内容就是整个stdin流的全部数据。sys.stdin.read()会一次性读完所有内容,此时stdin流直接到达EOF状态。后续调用input()时,因为没有更多内容可以读取,直接抛出EOFError,自然不会显示提示符。
2. 交互式终端场景
当执行python3 test.py -p 9998时,sys.stdin.read()会一直等待用户输入,直到用户按下Ctrl+D(触发EOF)才会返回。此时:
- 若用户在按下Ctrl+D前输入了内容,这些内容会被全部读取到
buffer中并发送给服务器; - 但
sys.stdin.read()执行完毕后,stdin流的指针停在EOF位置。后续调用input()时,虽然终端还能接收用户输入,但Python的stdin流已经处于EOF状态,input()无法正确读取新输入的内容,表现为输入后无响应。
本质上,sys.stdin是一个单向的字节流,一旦被读取到EOF,后续读取操作要么立即返回EOF(触发异常),要么无限等待永远不会到来的新数据。而input()依赖于从stdin流中读取完整的一行,当流已耗尽时,自然无法正常工作。
内容的提问来源于stack exchange,提问作者milanHrabos
相关产品推荐
相关产品推荐

