【问题标题】:Python subprocess: stderr only saving the first line. Why?Python 子进程:stderr 只保存第一行。为什么?
【发布时间】:2013-07-14 23:44:24
【问题描述】:

我在 Python 中运行 tcpdump,我想知道有多少 数据包 被内核丢弃。。 p>

在命令行上运行时,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,并显示了 commnunicate() 的输出:

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

...仍然只是第一行!

编辑 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 bytes\n221 packets captured\n221 packets received by filter\n0 packets dropped by kernel\n')

【问题讨论】:

  • 绝对没有。是空的
  • 您是否尝试过在subprocess 调用中进行输出重定向?即tcpdump ... > tcpdumpSTDOUT
  • 嗯...究竟如何?你能给我举个例子吗?谢谢:)
  • 我刚做了。想想你将如何在 shell 上进行 I/O 重定向,并在你的 subprocess 调用中做到这一点。所以在 shell 上,当你调用 tcpdump -w myPackets.cap -i eth0 ip 时。实际上,现在重新阅读您的帖子,我发现sudo 不是您在subprocess 中通话的一部分。您是否以 root 身份运行 python 脚本?
  • 是的,我在运行脚本时已经是 root。不过我没听懂你在说什么。当我使用参数stdout=f_outstd_err=f_err 调用subprocess.Popen() 时,我认为重定向已经发生了。我在这里错过了什么?

标签: python process subprocess tcpdump


【解决方案1】:

问题是我在进程上调用了kill() 而不是terminate()。对于后者,所有消息都存储在我指定为stderr 的任何内容中(tcpdump,出于某种原因,写入标准错误而不是标准输出)。

因此,如果它可能对其他人有所帮助,我决定将 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

【讨论】:

    【解决方案2】:

    使用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
    

    【讨论】:

    • 你比我早了 39 秒 :) 一件事:为什么 SIGTERM 应该生成僵尸?我以为只有 SIGKILL 可以。
    • @RickyRobinson:信号无关紧要。即使进程在没有任何外部信号的情况下自行完成,您也应该调用 proc.wait()
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-04-28
    • 1970-01-01
    • 2019-05-29
    • 1970-01-01
    • 2018-09-07
    相关资源
    最近更新 更多