Python子进程:stderr只保存第一行.为什么?



我正在Python中运行tcpdump,我想知道内核丢弃了多少数据包

当在命令行上运行时,tcpdump看起来像这样:

me@mypc:$ sudo tcpdump -w myPackets.cap -i eth0 ip
tcpdump: listening on eth2, link-type EN10MB (Ethernet), capture size 65535 bytes
^C28 packets captured
28 packets received by filter
0 packets dropped by kernel

这就是我在Python脚本中调用tcpdump的方式:

f_out = open("tcpdumpSTDOUT", "w")
f_err = open("tcpdumpSTDERR", "w")
tcpdumpProcess = subprocess.Popen(['tcpdump',
                        '-w', 'myPackets.cap', '-i', 'eth0', '-n','ip'],
                        stdout=f_out,
                        stderr=f_err)
# a few seconds later:
tcpdumpProcess.kill()
f_in.close()
f_out.close()

现在,如果我看tcpdumpSTDERR,我看到通常输出行中的第一行

tcpdump:在eth0上侦听,链路类型EN10MB(以太网),捕获大小65535字节

剩下的都在哪里?

编辑我尝试了一种不同的方法:

>>> myProcess = subprocess.Popen("tcpdump -w myPackets.cap -i eth2 ip",  shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
>>> myProcess.communicate()

然后我从另一个shell中杀死了tcpdump,commnunitate()的输出显示为:

('', 'tcpdump: listening on eth2, link-type EN10MB (Ethernet), capture size 65535 bytesn')

仍然只是第一行!

编辑2有趣的是:

>>> import shlex
>>> a = subprocess.Popen(shlex.split("tcpdump -w myPackets.cap -i eth2 ip"),  stdout=subprocess.PIPE, stderr=subprocess.PIPE)
>>> a.terminate()
>>> a.communicate()
('', 'tcpdump: listening on eth2, link-type EN10MB (Ethernet), capture size 65535 bytesn221 packets capturedn221 packets received by filtern0 packets dropped by kerneln')

使用proc.terminate()而不是proc.kill():

import shlex
import subprocess
import time
with open("tcpdumpSTDERR", "wb") as f_err: # close the file automatically
    proc = subprocess.Popen(shlex.split("tcpdump -w myPackets.cap -i eth2 ip"),
                            stderr=f_err)
time.sleep(2)    # wait a few seconds
proc.terminate() # send SIGTERM instead of SIGKILL
proc.wait()      # avoid zombies

问题是我在进程上调用了kill()而不是terminate()。对于后者,所有消息都存储在我指定的stderr中(出于某种原因,tcpdump写入stderr,而不是stdout)。

因此,为了避免对其他人有所帮助,我决定将stderr重定向到subprocess.PIPE,并直接用Python:解析字符串

>>> tcpdumpProcess = subprocess.Popen(['tcpdump',
                        '-w', 'myPackets.cap', '-i', 'eth0', '-n','ip'],
                        stdout=subprocess.PIPE,
                        stderr=subprocess.PIPE)
>>> tcpdumpProcess.terminate()
# stdout in [0], stderr in [1]
>>> tcpdump_stderr = tcpdumpProcess.communicate()[1]
>>> print tcpdump_stderr
tcpdump: listening on eth2, link-type EN10MB (Ethernet), capture size 65535 bytes
40 packets captured
40 packets received by filter
0 packets dropped by kernel

最新更新