在Python中捕获终端输出

4
我希望能够做类似于这里第二个答案的事情(但不完全相同):在Linux中工作时模拟Python中的Ctrl-C键盘中断。这种方法更简单,我认为我只是缺少了一些东西。比如说,从一个Python脚本中,我想要调用“ping”,并在第10次后终止它。我正试图像上面链接中所示的那样去实现它。
p = subprocess.Popen(['ping', 'google.com'], stdout=subprocess.PIPE)
for line in p.stdout:
  print line
  if re.search('10', line):
    break
os.kill(p.pid, signal.SIGINT)

但它不起作用。

我还希望正常的 'ping' 输出能够显示。我该怎么做?

编辑:实际上我想做的不是 'ping'。 我只是用它作为具有连续输出的命令的示例,而这些命令最终需要终止。

更具体地说,我正在运行旧版本的BitTorrent(来自此处第3个答案的v5.0.9:Where to find BitTorrent source code?),并通过python脚本调用它。 bittorrent-console.py是一个简单的终端版本,因此称之为“console”。 它会定期输出多行,类似于:

saving:       filename
file size:    blah
percent done: 100.0
blah:         blahblah

我实际上是通过以下方式调用它:

subprocess.call(['./bittorrent-console.py', 'something.torrent'])

我希望当“完成百分比”显示为100.0时,自动终止它。
编辑:我正在CentOS上运行Python 2.6。

1
请注意,10可能是IP地址的一部分。 - Ron Klein
1
是的,我意识到了,但是假设地址或时间中没有10。但再次强调,这只是一个例子。它本可以是'if re.search('icmp_seq=10''。 - joalT
请注意,subprocess.Popensubprocess.call在一个非常重要的细节上有所不同。前者是异步的,而后者是同步的。在后一种情况下,当您调用os.kill()时,应用程序已经死亡,os.kill()根本没有任何效果。 - Robᵩ
啊,是的。那确实是真的。我想继续使用 call,因为像下面的答案一样输出没有延迟。它只是调用了 bittorrent-console 并让它保持运行状态。我不知道如何在下载完成后终止它,除非通过键盘中断。 - joalT
您是否知道在脚本中,在“call”后面,是否有一种方法可以获取刚刚调用的进程ID? - joalT
顺便提一下,您可以使用其“share”链接分享特定的答案。这就是我们可以在具有不同排序选项(活跃/最旧的/投票)的同时查看相同答案的方式。 - Ron Klein
2个回答

3
首先,您应该使用 p.stdout.readline。我不确定为什么,但是使用 for line in p.stdout 似乎无法刷新。可能存在缓冲区。
其次,您应该使用 sys.stdout.write(line),因为 print 总会附加一些内容。在 Python 3 中,您可以使用 print(line, end="")
另外,您应该优先使用 p.kill 而不是 os.kill。我不确定为什么您要使用 os.kill
import os
import signal
import subprocess
import sys

p = subprocess.Popen(['ping', 'google.com'], stdout=subprocess.PIPE)
while True:
    line = p.stdout.readline()

    sys.stdout.write(line)
    sys.stdout.flush()
    if '10' in line:
        break

p.kill()

我认为这会起作用。对于“ping”示例有效。稍后我会在我真正工作的项目上尝试它。 - joalT
这个输出效果不太好。由于 bittorrent-console 一次打印出多行,因此每行写入和刷新需要时间。有没有办法我只使用 subprocess.call 捕获“100.0”并终止 bittorrent-console? - joalT
不,没有其他方法。你可以在subprocess.Popen中传递bufsize=0,但除此之外没有其他办法(如果行是逐步给出的,则解析子行可能有所帮助,但我怀疑这一点)。 - Veedrac
好的,你的回答确实解决了我的问题。不过我不想在终端输出上看到缓慢的更新。我需要它仍然保持实时性。我想我得另寻他法了。 - joalT

2
这个代码可以完美地实现您想要的功能,并且在我的OS X机器上运行良好:
import subprocess
import re

def get_output(cmd, until):
    p = subprocess.Popen(cmd, stdout=subprocess.PIPE)
    ret = []
    while True:
        line = p.stdout.readline()
        ret.append(line)
        if re.search(until, line):
            break
    p.kill()
    return ret

 print ''.join(get_output(['ping', 'google.com'], until='10'))

再次强调,10ping只是示例。 - joalT
我不明白这个答案为什么不能解决你的问题。 - Erik Kaplun
当然可以,但您使用了一个范围进行迭代。我希望在遇到模式“10”时终止。 - joalT
1
我以为你的意思是“输出10行后”。无论如何,我已经相应地更新了我的答案。 - Erik Kaplun

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接