从伪终端中的本地进程超时读取
read with timeout from local process in pseudo terminal
我要e。 G。阅读 "tcpdump":
打印的第一行
tcpdump: listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
使用"ptyprocess"(上下文:本地进程,涉及终端)和select() 等待超时的新数据:
import logging
from ptyprocess import PtyProcess
from select import select
logging.basicConfig(
level=logging.DEBUG,
format="%(asctime)s %(name)s %(message)s")
pty_process = PtyProcess.spawn(
argv=["sudo", "tcpdump", "-w", "capture.pcap", "-i", "enp0s3"],
echo=True)
while True:
rlist, _, _ = select([pty_process.fd], [], [], 1)
if pty_process.fd in rlist:
try:
data = pty_process.read(1)
except EOFError:
logging.debug("EOF")
break
logging.debug("read: %r", data)
else:
logging.debug("timeout")
对于 Python 3.x(使用 3.6.10 和 3.8.1 测试)此代码读取由 "tcpdump".
打印出的上述行
对于 Python 2.x(使用 2.7.17 测试)此代码仅读取第一个字符 "t",然后 select() 超时。我还观察到,对于第一个 运行,读取了不止一个字符,但不是全部。
在 Debian 10 上测试。
在我读取 Python 2 中的下一个字符之前,如何使用 select() 和 "ptyprocess" 的超时(或类似的东西)来等待新数据?
更新 1:
strace 显示以下差异:
Python 2:
select(6, [5], [], [], {tv_sec=1, tv_usec=0}) = 1 (in [5], left {tv_sec=0, tv_usec=999993})
read(5, "tcpdump: listening on enp0s3, li"..., 8192) = 86
Python 3:
select(6, [5], [], [], {tv_sec=1, tv_usec=0}) = 1 (in [5], left {tv_sec=0, tv_usec=999994})
read(5, "t", 1) = 1
我。 e.对于 Python 2,调用 read(..., 8192),对于 Python 3,调用 read(..., 1)。我怎样才能实现, for Python 2 also read(..., 1) is called?
更新 2:
问题与"tcpdump"无关,也可以这样重现:
import logging
from ptyprocess import PtyProcess
from select import select
logging.basicConfig(
level=logging.DEBUG,
format="%(asctime)s %(name)s %(message)s")
pty_process = PtyProcess.spawn(
argv=["bash", "-c", "echo 123 ; sleep 3"],
echo=True)
while True:
rlist, _, _ = select([pty_process.fd], [], [], 1)
if pty_process.fd in rlist:
try:
data = pty_process.read(1)
except EOFError:
logging.debug("EOF")
break
logging.debug("read: %r", data)
else:
logging.debug("timeout")
Python 2 输出:
2020-04-23 12:51:27,126 root read: '1'
2020-04-23 12:51:28,193 root timeout
2020-04-23 12:51:29,204 root timeout
2020-04-23 12:51:30,129 root read: '2'
2020-04-23 12:51:30,129 root read: '3'
2020-04-23 12:51:30,129 root read: '\r'
2020-04-23 12:51:30,130 root read: '\n'
2020-04-23 12:51:30,130 root EOF
Python 3 输出:
2020-04-23 12:51:23,106 root read: b'1'
2020-04-23 12:51:23,107 root read: b'2'
2020-04-23 12:51:23,107 root read: b'3'
2020-04-23 12:51:23,107 root read: b'\r'
2020-04-23 12:51:23,107 root read: b'\n'
2020-04-23 12:51:24,109 root timeout
2020-04-23 12:51:25,109 root timeout
2020-04-23 12:51:26,109 root EOF
PtyProcess.read()
呼叫 self.fileobj.read1()
。 PtyProcess.fileobj
的类型为 BufferedRWPair
。 BufferedRWPair.read1()
代表 BufferedRWPair.reader.read1()
。 BufferedRWPair
的构造函数从参数 reader
创建一个 BufferedReader
对象。
在 Python 2.7.16 Modules/_io/bufferedio.c/buffered_read1()
中调用 _bufferedreader_fill_buffer(self)
,它执行:
len = self->buffer_size - start;
n = _bufferedreader_raw_read(self, self->buffer + start, len);
在 Python 3.8.1 Modules/_io/bufferedio.c/_io__Buffered_read1_impl()
中调用:
r = _bufferedreader_raw_read(self, PyBytes_AS_STRING(res), n);
换句话说,在 Python 3 BufferedReader.read1(n)
中原始读取 n 个字节,而在 Python 2 中它读取更多字节来填充缓冲区。
不可能像问题中发布的代码那样,将作用于缓冲区的 read(1) 与作用于底层文件描述符的 select()
结合使用。
以下代码使用 pexpect 而不是 ptyprocess,允许超时读取:
import logging
import pexpect
logging.basicConfig(
level=logging.DEBUG,
format="%(asctime)s %(name)s %(message)s")
child = pexpect.spawn("bash -c 'echo 123 ; sleep 3'")
while True:
try:
data = child.read_nonblocking(size=1, timeout=1)
logging.debug("read: %r", data)
except pexpect.TIMEOUT:
logging.debug("timeout")
except pexpect.EOF:
logging.debug("EOF")
break
输出:
2020-04-26 14:54:56,006 root read: '1'
2020-04-26 14:54:56,007 root read: '2'
2020-04-26 14:54:56,007 root read: '3'
2020-04-26 14:54:56,007 root read: '\r'
2020-04-26 14:54:56,007 root read: '\n'
2020-04-26 14:54:57,009 root timeout
2020-04-26 14:54:58,010 root timeout
2020-04-26 14:54:59,008 root EOF
我要e。 G。阅读 "tcpdump":
打印的第一行tcpdump: listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
使用"ptyprocess"(上下文:本地进程,涉及终端)和select() 等待超时的新数据:
import logging
from ptyprocess import PtyProcess
from select import select
logging.basicConfig(
level=logging.DEBUG,
format="%(asctime)s %(name)s %(message)s")
pty_process = PtyProcess.spawn(
argv=["sudo", "tcpdump", "-w", "capture.pcap", "-i", "enp0s3"],
echo=True)
while True:
rlist, _, _ = select([pty_process.fd], [], [], 1)
if pty_process.fd in rlist:
try:
data = pty_process.read(1)
except EOFError:
logging.debug("EOF")
break
logging.debug("read: %r", data)
else:
logging.debug("timeout")
对于 Python 3.x(使用 3.6.10 和 3.8.1 测试)此代码读取由 "tcpdump".
打印出的上述行对于 Python 2.x(使用 2.7.17 测试)此代码仅读取第一个字符 "t",然后 select() 超时。我还观察到,对于第一个 运行,读取了不止一个字符,但不是全部。
在 Debian 10 上测试。
在我读取 Python 2 中的下一个字符之前,如何使用 select() 和 "ptyprocess" 的超时(或类似的东西)来等待新数据?
更新 1:
strace 显示以下差异:
Python 2:
select(6, [5], [], [], {tv_sec=1, tv_usec=0}) = 1 (in [5], left {tv_sec=0, tv_usec=999993})
read(5, "tcpdump: listening on enp0s3, li"..., 8192) = 86
Python 3:
select(6, [5], [], [], {tv_sec=1, tv_usec=0}) = 1 (in [5], left {tv_sec=0, tv_usec=999994})
read(5, "t", 1) = 1
我。 e.对于 Python 2,调用 read(..., 8192),对于 Python 3,调用 read(..., 1)。我怎样才能实现, for Python 2 also read(..., 1) is called?
更新 2:
问题与"tcpdump"无关,也可以这样重现:
import logging
from ptyprocess import PtyProcess
from select import select
logging.basicConfig(
level=logging.DEBUG,
format="%(asctime)s %(name)s %(message)s")
pty_process = PtyProcess.spawn(
argv=["bash", "-c", "echo 123 ; sleep 3"],
echo=True)
while True:
rlist, _, _ = select([pty_process.fd], [], [], 1)
if pty_process.fd in rlist:
try:
data = pty_process.read(1)
except EOFError:
logging.debug("EOF")
break
logging.debug("read: %r", data)
else:
logging.debug("timeout")
Python 2 输出:
2020-04-23 12:51:27,126 root read: '1'
2020-04-23 12:51:28,193 root timeout
2020-04-23 12:51:29,204 root timeout
2020-04-23 12:51:30,129 root read: '2'
2020-04-23 12:51:30,129 root read: '3'
2020-04-23 12:51:30,129 root read: '\r'
2020-04-23 12:51:30,130 root read: '\n'
2020-04-23 12:51:30,130 root EOF
Python 3 输出:
2020-04-23 12:51:23,106 root read: b'1'
2020-04-23 12:51:23,107 root read: b'2'
2020-04-23 12:51:23,107 root read: b'3'
2020-04-23 12:51:23,107 root read: b'\r'
2020-04-23 12:51:23,107 root read: b'\n'
2020-04-23 12:51:24,109 root timeout
2020-04-23 12:51:25,109 root timeout
2020-04-23 12:51:26,109 root EOF
PtyProcess.read()
呼叫 self.fileobj.read1()
。 PtyProcess.fileobj
的类型为 BufferedRWPair
。 BufferedRWPair.read1()
代表 BufferedRWPair.reader.read1()
。 BufferedRWPair
的构造函数从参数 reader
创建一个 BufferedReader
对象。
在 Python 2.7.16 Modules/_io/bufferedio.c/buffered_read1()
中调用 _bufferedreader_fill_buffer(self)
,它执行:
len = self->buffer_size - start;
n = _bufferedreader_raw_read(self, self->buffer + start, len);
在 Python 3.8.1 Modules/_io/bufferedio.c/_io__Buffered_read1_impl()
中调用:
r = _bufferedreader_raw_read(self, PyBytes_AS_STRING(res), n);
换句话说,在 Python 3 BufferedReader.read1(n)
中原始读取 n 个字节,而在 Python 2 中它读取更多字节来填充缓冲区。
不可能像问题中发布的代码那样,将作用于缓冲区的 read(1) 与作用于底层文件描述符的 select()
结合使用。
以下代码使用 pexpect 而不是 ptyprocess,允许超时读取:
import logging
import pexpect
logging.basicConfig(
level=logging.DEBUG,
format="%(asctime)s %(name)s %(message)s")
child = pexpect.spawn("bash -c 'echo 123 ; sleep 3'")
while True:
try:
data = child.read_nonblocking(size=1, timeout=1)
logging.debug("read: %r", data)
except pexpect.TIMEOUT:
logging.debug("timeout")
except pexpect.EOF:
logging.debug("EOF")
break
输出:
2020-04-26 14:54:56,006 root read: '1'
2020-04-26 14:54:56,007 root read: '2'
2020-04-26 14:54:56,007 root read: '3'
2020-04-26 14:54:56,007 root read: '\r'
2020-04-26 14:54:56,007 root read: '\n'
2020-04-26 14:54:57,009 root timeout
2020-04-26 14:54:58,010 root timeout
2020-04-26 14:54:59,008 root EOF