I want to e. g. read the first line printed out by "tcpdump":
tcpdump: listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
using "ptyprocess" (context: local process, terminal involved) and select() to wait for new data with a timeout:
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")
For Python 3.x (tested with 3.6.10 and 3.8.1) this code reads the above mentioned line printed out by "tcpdump".
For Python 2.x (tested with 2.7.17) this code only reads the first character "t" and after that select() times out. I also observed, that for a first run, more than one character was read, but not all.
Tested on Debian 10.
How can I use select() with a timeout (or something similar) with "ptyprocess" to wait for new data, before I read the next character in Python 2?
Update 1:
strace shows the following difference:
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
I. e. for Python 2, read(..., 8192) is called and for Python 3, read(..., 1). How can I achieve, that for Python 2 also read(..., 1) is called?
Update 2:
The problem is independent from "tcpdump" and can also be reproduced like this:
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 output:
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 output:
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