首页 > 解决方案 > subprocess.Popen():在子进程执行期间更改标准错误

问题描述

目标:我试图将一个 Python 脚本放在一起,以捕获由于执行代码块而发生的网络流量。为简单起见,假设我想记录调用socket.gethostbyname('example.com'). 注意:我不能简单地tcpdumpgethostbyname()返回时终止,因为我想要测量的实际代码块触发了其他外部代码,而且我无法确定这个外部代码何时完成执行(所以我必须让tcpdump运行“足够长”因为我很可能记录了此外部代码生成的所有流量)。

方法:我正在使用subprocessto start tcpdump,告诉在几秒钟后使用它的和选项tcpdump终止自身,例如:duration-G-W

duration = 15
nif = 'en0'
pcap = 'dns.pcap'
cmd = ['tcpdump', '-G', str(duration), '-W', '1', '-i', nif, '-w', pcap]
tcpdump_proc = subprocess.Popen(cmd)
socket.gethostbyname('example.com')
time.sleep(duration + 5) # sleep longer than tcpdump is running

这样做的问题是,Popen()return before tcpdump完全启动并运行,因此gethostbyname()不会捕获由调用产生的部分/全部流量。我显然可以time.sleep(x)在调用之前添加一个gethostbyname()来给tcpdump一些时间来启动,但这不是一个可移植的解决方案(我不能随意选择一些x < duration,因为一个强大的系统会比一个不太强大的系统更早地开始捕获数据包)。

为了解决这个问题,我的想法是解析tcpdump的输出以查找何时将以下内容写入其stderr,因为这似乎表明捕获已启动并完全运行:

tcpdump: listening on en0, link-type EN10MB (Ethernet), capture size 262144 bytes

因此我需要附加到stderr,但问题是我不想承诺读取它的所有输出,因为我需要我的代码继续执行我想要测量的代码块(gethostbyname()在这个例子中)而不是陷入从 读取的循环中stderr

我可以通过添加一个阻止主线程继续gethostbyname()调用的信号量来解决这个问题,并且当stderr它从stderr如果可能的话,我想保持代码单线程。

据我了解,这是一个很大的 NONOsubprocess.PIPE用于stderr并且stdout不承诺读取所有输出,因为当缓冲区填满时,孩子最终会阻塞。但是,如果您只对读取输出的第一部分感兴趣,您可以“分离”(销毁?)管道中间执行吗?本质上,我想得到这样的结果:

duration = 15
nif = 'en0'
pcap = 'dns.pcap'
cmd = ['tcpdump', '-G', str(duration), '-W', '1', '-i', nif, '-w', pcap]
tcpdump_proc = subprocess.Popen(cmd, stderr=subprocess.PIPE, text=True)
for l in tcpdump_proc.stderr:
    if 'tcpdump: listening on' in l:
        break
socket.gethostbyname('example.com')
time.sleep(duration) # sleep at least as long as tcpdump is running

我还需要在块中添加什么if来“重新分配”负责阅读的人stderr?我可以stderr回到None( tcpdump_proc.stderr = None) 吗?还是我应该打电话tcpdump_proc.stderr.close()tcpdump如果我这样做会提前终止)?

也很可能是我错过了一些明显的东西,并且有更好的方法来实现我想要的 - 如果是这样,请赐教:)。

提前致谢 :)

标签: pythonsubprocesspipetcpdump

解决方案


收到消息后,您可以在 stderr 上使用detach()or :close()listening on

import subprocess
import time

duration = 10
nif = 'eth0'
pcap = 'dns.pcap'
cmd = ['tcpdump', '-G', str(duration), '-W', '1', '-i', nif, '-w', pcap]

proc = subprocess.Popen(
    cmd, shell=False, stderr=subprocess.PIPE, bufsize=1, text=True
)
for i, line in enumerate(proc.stderr):
    print('read %d lines from stderr' % i)
    if 'listening on' in line:
        print('detach stderr!')
        proc.stderr.detach()
        break

while proc.poll() is None:
    print("doing something else while tcpdump is runnning!")
    time.sleep(2)

print(proc.returncode)
print(proc.stderr.read())

出去:

read 0 lines from stderr
detach stderr!
doing something else while tcpdump is runnning!
doing something else while tcpdump is runnning!
doing something else while tcpdump is runnning!
doing something else while tcpdump is runnning!
doing something else while tcpdump is runnning!
doing something else while tcpdump is runnning!
0
Traceback (most recent call last):
  File "x.py", line 24, in <module>
    print(proc.stderr.read())
ValueError: underlying buffer has been detached

笔记:

我还没有检查 stderr 数据到底发生了什么但是分离 stderr 似乎对 tcpdump 没有任何影响。


推荐阅读