停止读取 Python 中的进程输出而不挂起?

发布于 2024-10-06 20:40:42 字数 2672 浏览 2 评论 0原文

我有一个Linux的Python程序几乎看起来像这样:

import os
import time

process = os.popen("top").readlines()

time.sleep(1)

os.popen("killall top")

print process

程序挂在这一行:

process = os.popen("top").readlines()

这发生在不断更新输出的工具中,如“Top”

我最好的试验:

import os
import time
import subprocess

process = subprocess.Popen('top')

time.sleep(2)

os.popen("killall top")

print process

它比第一个程序工作得更好(它被杀死了),但它返回:

<subprocess.Popen object at 0x97a50cc>

第二次试验:

import os
import time
import subprocess

process = subprocess.Popen('top').readlines()

time.sleep(2)

os.popen("killall top")

print process

与第一次相同。它由于“readlines()”而挂起

它的返回应该是这样的:

top - 05:31:15 up 12:12,  5 users,  load average: 0.25, 0.14, 0.11
Tasks: 174 total,   2 running, 172 sleeping,   0 stopped,   0 zombie
Cpu(s):  9.3%us,  3.8%sy,  0.1%ni, 85.9%id,  0.9%wa,  0.0%hi,  0.0%si,  0.0%st
Mem:   1992828k total,  1849456k used,   143372k free,   233048k buffers
Swap:  4602876k total,        0k used,  4602876k free,  1122780k cached

  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND            
31735 Barakat   20   0  246m  52m  20m S 19.4  2.7  13:54.91 totem              
 1907 root      20   0 91264  45m  15m S  1.9  2.3  38:54.14 Xorg               
 2138 Barakat   20   0 17356 5368 4284 S  1.9  0.3   3:00.15 at-spi-registry    
 2164 Barakat    9 -11  164m 7372 6252 S  1.9  0.4   2:54.58 pulseaudio         
 2394 Barakat   20   0 27212 9792 8256 S  1.9  0.5   6:01.48 multiload-apple    
 6498 Barakat   20   0 56364  30m  18m S  1.9  1.6   0:03.38 pyshell            
    1 root      20   0  2880 1416 1208 S  0.0  0.1   0:02.02 init               
    2 root      20   0     0    0    0 S  0.0  0.0   0:00.02 kthreadd           
    3 root      RT   0     0    0    0 S  0.0  0.0   0:00.12 migration/0        
    4 root      20   0     0    0    0 S  0.0  0.0   0:02.07 ksoftirqd/0        
    5 root      RT   0     0    0    0 S  0.0  0.0   0:00.00 watchdog/0         
    9 root      20   0     0    0    0 S  0.0  0.0   0:01.43 events/0           
   11 root      20   0     0    0    0 S  0.0  0.0   0:00.00 cpuset             
   12 root      20   0     0    0    0 S  0.0  0.0   0:00.02 khelper            
   13 root      20   0     0    0    0 S  0.0  0.0   0:00.00 netns              
   14 root      20   0     0    0    0 S  0.0  0.0   0:00.00 async/mgr          
   15 root      20   0     0    0    0 S  0.0  0.0   0:00.00 pm

并保存在变量“process”中。伙计们,我现在真的陷入困境了吗?

I have a Python program for Linux almost looks like this one :

import os
import time

process = os.popen("top").readlines()

time.sleep(1)

os.popen("killall top")

print process

the program hangs in this line :

process = os.popen("top").readlines()

and that happens in the tools that keep update outputting like "Top"

my best trials :

import os
import time
import subprocess

process = subprocess.Popen('top')

time.sleep(2)

os.popen("killall top")

print process

it worked better than the first one (it's kelled ), but it returns :

<subprocess.Popen object at 0x97a50cc>

the second trial :

import os
import time
import subprocess

process = subprocess.Popen('top').readlines()

time.sleep(2)

os.popen("killall top")

print process

the same as the first one. It hanged due to "readlines()"

Its returning should be like this :

top - 05:31:15 up 12:12,  5 users,  load average: 0.25, 0.14, 0.11
Tasks: 174 total,   2 running, 172 sleeping,   0 stopped,   0 zombie
Cpu(s):  9.3%us,  3.8%sy,  0.1%ni, 85.9%id,  0.9%wa,  0.0%hi,  0.0%si,  0.0%st
Mem:   1992828k total,  1849456k used,   143372k free,   233048k buffers
Swap:  4602876k total,        0k used,  4602876k free,  1122780k cached

  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND            
31735 Barakat   20   0  246m  52m  20m S 19.4  2.7  13:54.91 totem              
 1907 root      20   0 91264  45m  15m S  1.9  2.3  38:54.14 Xorg               
 2138 Barakat   20   0 17356 5368 4284 S  1.9  0.3   3:00.15 at-spi-registry    
 2164 Barakat    9 -11  164m 7372 6252 S  1.9  0.4   2:54.58 pulseaudio         
 2394 Barakat   20   0 27212 9792 8256 S  1.9  0.5   6:01.48 multiload-apple    
 6498 Barakat   20   0 56364  30m  18m S  1.9  1.6   0:03.38 pyshell            
    1 root      20   0  2880 1416 1208 S  0.0  0.1   0:02.02 init               
    2 root      20   0     0    0    0 S  0.0  0.0   0:00.02 kthreadd           
    3 root      RT   0     0    0    0 S  0.0  0.0   0:00.12 migration/0        
    4 root      20   0     0    0    0 S  0.0  0.0   0:02.07 ksoftirqd/0        
    5 root      RT   0     0    0    0 S  0.0  0.0   0:00.00 watchdog/0         
    9 root      20   0     0    0    0 S  0.0  0.0   0:01.43 events/0           
   11 root      20   0     0    0    0 S  0.0  0.0   0:00.00 cpuset             
   12 root      20   0     0    0    0 S  0.0  0.0   0:00.02 khelper            
   13 root      20   0     0    0    0 S  0.0  0.0   0:00.00 netns              
   14 root      20   0     0    0    0 S  0.0  0.0   0:00.00 async/mgr          
   15 root      20   0     0    0    0 S  0.0  0.0   0:00.00 pm

and save in the variable "process". Any I idea guys, I'm really stuck now ?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

疑心病 2024-10-13 20:40:42
#!/usr/bin/env python
"""Start process; wait 2 seconds; kill the process; print all process output."""
import subprocess
import tempfile
import time

def main():
    # open temporary file (it automatically deleted when it is closed)
    #  `Popen` requires `f.fileno()` so `SpooledTemporaryFile` adds nothing here
    f = tempfile.TemporaryFile() 

    # start process, redirect stdout
    p = subprocess.Popen(["top"], stdout=f)

    # wait 2 seconds
    time.sleep(2)

    # kill process
    #NOTE: if it doesn't kill the process then `p.wait()` blocks forever
    p.terminate() 
    p.wait() # wait for the process to terminate otherwise the output is garbled

    # print saved output
    f.seek(0) # rewind to the beginning of the file
    print f.read(), 
    f.close()

if __name__=="__main__":
    main()

仅打印部分输出的类似尾部的解决方案

您可以在另一个线程中读取进程输出,并将所需数量的最后几行保存在队列中:

import collections
import subprocess
import time
import threading

def read_output(process, append):
    for line in iter(process.stdout.readline, ""):
        append(line)

def main():
    # start process, redirect stdout
    process = subprocess.Popen(["top"], stdout=subprocess.PIPE, close_fds=True)
    try:
        # save last `number_of_lines` lines of the process output
        number_of_lines = 200
        q = collections.deque(maxlen=number_of_lines) # atomic .append()
        t = threading.Thread(target=read_output, args=(process, q.append))
        t.daemon = True
        t.start()

        #
        time.sleep(2)
    finally:
        process.terminate() #NOTE: it doesn't ensure the process termination

    # print saved lines
    print ''.join(q)

if __name__=="__main__":
    main()

此变体需要 q.append() 来是原子操作。否则输出可能会损坏。

signal.alarm() 解决方案

您可以使用 signal.alarm() 来调用指定超时后的 process.terminate() ,而不是在另一个线程中读取。尽管它可能无法与 subprocess 模块很好地交互。基于 @Alex Martelli 的回答

import collections
import signal
import subprocess

class Alarm(Exception):
    pass

def alarm_handler(signum, frame):
    raise Alarm

def main():
    # start process, redirect stdout
    process = subprocess.Popen(["top"], stdout=subprocess.PIPE, close_fds=True)

    # set signal handler
    signal.signal(signal.SIGALRM, alarm_handler)
    signal.alarm(2) # produce SIGALRM in 2 seconds

    try:
        # save last `number_of_lines` lines of the process output
        number_of_lines = 200
        q = collections.deque(maxlen=number_of_lines)
        for line in iter(process.stdout.readline, ""):
            q.append(line)
        signal.alarm(0) # cancel alarm
    except Alarm:
        process.terminate()
    finally:
        # print saved lines
        print ''.join(q)

if __name__=="__main__":
    main()

此方法仅适用于 *nix 系统。如果 process.stdout.readline() 不返回,它可能会阻塞。

threading.Timer 解决方案

import collections
import subprocess
import threading

def main():
    # start process, redirect stdout
    process = subprocess.Popen(["top"], stdout=subprocess.PIPE, close_fds=True)

    # terminate process in timeout seconds
    timeout = 2 # seconds
    timer = threading.Timer(timeout, process.terminate)
    timer.start()

    # save last `number_of_lines` lines of the process output
    number_of_lines = 200
    q = collections.deque(process.stdout, maxlen=number_of_lines)
    timer.cancel()

    # print saved lines
    print ''.join(q),

if __name__=="__main__":
    main()

这种方法也应该在 Windows 上工作。这里我使用了 process.stdout 作为可迭代对象;它可能会引入额外的输出缓冲,如果不需要,您可以切换到 iter(process.stdout.readline, "") 方法。如果进程未在 process.terminate() 处终止,则脚本将挂起。

无线程,无信号解决方案

import collections
import subprocess
import sys
import time

def main():
    args = sys.argv[1:]
    if not args:
        args = ['top']

    # start process, redirect stdout
    process = subprocess.Popen(args, stdout=subprocess.PIPE, close_fds=True)

    # save last `number_of_lines` lines of the process output
    number_of_lines = 200
    q = collections.deque(maxlen=number_of_lines)

    timeout = 2 # seconds
    now = start = time.time()    
    while (now - start) < timeout:
        line = process.stdout.readline()
        if not line:
            break
        q.append(line)
        now = time.time()
    else: # on timeout
        process.terminate()

    # print saved lines
    print ''.join(q),

if __name__=="__main__":
    main()

此变体既不使用线程,也不使用信号,但会在终端中产生乱码输出。如果 process.stdout.readline() 阻塞,它将阻塞。

#!/usr/bin/env python
"""Start process; wait 2 seconds; kill the process; print all process output."""
import subprocess
import tempfile
import time

def main():
    # open temporary file (it automatically deleted when it is closed)
    #  `Popen` requires `f.fileno()` so `SpooledTemporaryFile` adds nothing here
    f = tempfile.TemporaryFile() 

    # start process, redirect stdout
    p = subprocess.Popen(["top"], stdout=f)

    # wait 2 seconds
    time.sleep(2)

    # kill process
    #NOTE: if it doesn't kill the process then `p.wait()` blocks forever
    p.terminate() 
    p.wait() # wait for the process to terminate otherwise the output is garbled

    # print saved output
    f.seek(0) # rewind to the beginning of the file
    print f.read(), 
    f.close()

if __name__=="__main__":
    main()

Tail-like Solutions that print only the portion of the output

You could read the process output in another thread and save the required number of the last lines in a queue:

import collections
import subprocess
import time
import threading

def read_output(process, append):
    for line in iter(process.stdout.readline, ""):
        append(line)

def main():
    # start process, redirect stdout
    process = subprocess.Popen(["top"], stdout=subprocess.PIPE, close_fds=True)
    try:
        # save last `number_of_lines` lines of the process output
        number_of_lines = 200
        q = collections.deque(maxlen=number_of_lines) # atomic .append()
        t = threading.Thread(target=read_output, args=(process, q.append))
        t.daemon = True
        t.start()

        #
        time.sleep(2)
    finally:
        process.terminate() #NOTE: it doesn't ensure the process termination

    # print saved lines
    print ''.join(q)

if __name__=="__main__":
    main()

This variant requires q.append() to be atomic operation. Otherwise the output might be corrupted.

signal.alarm() solution

You could use signal.alarm() to call the process.terminate() after specified timeout instead of reading in another thread. Though it might not interact very well with the subprocess module. Based on @Alex Martelli's answer:

import collections
import signal
import subprocess

class Alarm(Exception):
    pass

def alarm_handler(signum, frame):
    raise Alarm

def main():
    # start process, redirect stdout
    process = subprocess.Popen(["top"], stdout=subprocess.PIPE, close_fds=True)

    # set signal handler
    signal.signal(signal.SIGALRM, alarm_handler)
    signal.alarm(2) # produce SIGALRM in 2 seconds

    try:
        # save last `number_of_lines` lines of the process output
        number_of_lines = 200
        q = collections.deque(maxlen=number_of_lines)
        for line in iter(process.stdout.readline, ""):
            q.append(line)
        signal.alarm(0) # cancel alarm
    except Alarm:
        process.terminate()
    finally:
        # print saved lines
        print ''.join(q)

if __name__=="__main__":
    main()

This approach works only on *nix systems. It might block if process.stdout.readline() doesn't return.

threading.Timer solution

import collections
import subprocess
import threading

def main():
    # start process, redirect stdout
    process = subprocess.Popen(["top"], stdout=subprocess.PIPE, close_fds=True)

    # terminate process in timeout seconds
    timeout = 2 # seconds
    timer = threading.Timer(timeout, process.terminate)
    timer.start()

    # save last `number_of_lines` lines of the process output
    number_of_lines = 200
    q = collections.deque(process.stdout, maxlen=number_of_lines)
    timer.cancel()

    # print saved lines
    print ''.join(q),

if __name__=="__main__":
    main()

This approach should also work on Windows. Here I've used process.stdout as an iterable; it might introduce an additional output buffering, you could switch to the iter(process.stdout.readline, "") approach if it is not desirable. if the process doesn't terminate on process.terminate() then the scripts hangs.

No threads, no signals solution

import collections
import subprocess
import sys
import time

def main():
    args = sys.argv[1:]
    if not args:
        args = ['top']

    # start process, redirect stdout
    process = subprocess.Popen(args, stdout=subprocess.PIPE, close_fds=True)

    # save last `number_of_lines` lines of the process output
    number_of_lines = 200
    q = collections.deque(maxlen=number_of_lines)

    timeout = 2 # seconds
    now = start = time.time()    
    while (now - start) < timeout:
        line = process.stdout.readline()
        if not line:
            break
        q.append(line)
        now = time.time()
    else: # on timeout
        process.terminate()

    # print saved lines
    print ''.join(q),

if __name__=="__main__":
    main()

This variant use neither threads, no signals but it produces garbled output in the terminal. It will block if process.stdout.readline() blocks.

花开柳相依 2024-10-13 20:40:42

我建议不要使用“top”,而是使用“ps”,它会为您提供相同的信息,但只会提供一次,而不是永远每秒一次。

您还需要在 ps 中使用一些标志,我倾向于使用“ps aux”

Instead of using "top" I suggest using "ps" which will give you the same information, but only once instead of once a second for all eternity.

You'll need to also use some flags with ps, I tend to use "ps aux"

笑梦风尘 2024-10-13 20:40:42

我要做的,而不是这种方法,是检查您试图从中获取信息的程序,并确定该信息的最终来源。它可能是 API 调用或设备节点。然后,编写一些从同一源获取它的Python。这消除了“抓取”“熟”数据的问题和开销。

What I would do, rather than this approach, is to examine the program you are trying to get information from and determine the ultimate source of that information. It may be an API call or device node. Then, write some python that gets it from the same source. That eliminates the problems and overhead of "scraping" "cooked" data.

自由如风 2024-10-13 20:40:42

(JF Sebastian,你的代码工作得很好,我认为它比我的解决方案更好=))

我已经用另一种方式解决了它。

我没有直接在终端上输出,而是将其放入文件“tmp_file”中:

top >> tmp_file

然后我使用工具“cut”将其输出“这是顶部输出”作为进程的值

cat tmp_file

,并且它做了我想要它做的事情。这是最终的代码:

import os
import subprocess
import time

subprocess.Popen("top >> tmp_file",shell = True)

time.sleep(1)

os.popen("killall top")

process = os.popen("cat tmp_file").read()

os.popen("rm tmp_file")

print process

# Thing better than nothing =)

非常感谢大家的帮助

( J.F. Sebastian your codes work great , I think it's better than my solution =) )

I've solved it using another way.

Instead of making the output directly on the terminal I make it into a file "tmp_file" :

top >> tmp_file

then I used the tool "cut" to make its output "which is top output" as process's value

cat tmp_file

and it did what I want it to do .This is the final code:

import os
import subprocess
import time

subprocess.Popen("top >> tmp_file",shell = True)

time.sleep(1)

os.popen("killall top")

process = os.popen("cat tmp_file").read()

os.popen("rm tmp_file")

print process

# Thing better than nothing =)

Thank you so much guys for help

人间不值得 2024-10-13 20:40:42

事实上,如果您填充输出缓冲区,您最终会得到一些答案。因此,一种解决方案是用大量垃圾输出填充缓冲区(大约 6000 个字符,bufsize=1)。

比方说,你有一个在 sys.stdout: 上编写的 python 脚本,而不是 top:

GARBAGE='.\n'
sys.stdout.write(valuable_output)
sys.stdout.write(GARBAGE*3000)

在启动器方面,而不是简单的 process.readline():

GARBAGE='.\n'
line=process.readline()
while line==GARBAGE:
   line=process.readline()

很确定它有点脏,因为 2000 依赖于子进程实现,但它工作正常而且非常简单。设置除 bufsize=1 之外的任何值都会使情况变得更糟。

In facts, if you fill the output buffer, you'll end with some answer. So one solution is to fill the buffer with a large garbage output (~6000 character with bufsize=1).

Let's say, instead of top, you have a python script that write on sys.stdout:

GARBAGE='.\n'
sys.stdout.write(valuable_output)
sys.stdout.write(GARBAGE*3000)

On the launcher side, instead of simple process.readline():

GARBAGE='.\n'
line=process.readline()
while line==GARBAGE:
   line=process.readline()

Quite sure it is a bit dirty as 2000 is dependant on subprocess implementation, but it works fine and is very simple. setting anything but bufsize=1 make the matter worse.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文