我正在尝试将shell脚本移植到可读性更强的python版本。原来的shell脚本使用“&”在后台启动几个进程(实用程序、监视器等)。如何在python中实现相同的效果?我希望这些进程在python脚本完成时不死亡。我确信这与守护进程的概念有关,但我不知道如何轻松地做到这一点。


当前回答

我还没有尝试过这个,但使用.pyw文件而不是.py文件应该有帮助。Pyw文件没有控制台,所以理论上它不应该像后台进程一样出现和工作。

其他回答

我还没有尝试过这个,但使用.pyw文件而不是.py文件应该有帮助。Pyw文件没有控制台,所以理论上它不应该像后台进程一样出现和工作。

使用subprocess. popen()和close_fds=True参数,这将允许派生的子进程从Python进程本身分离出来,即使在Python退出后也能继续运行。

https://gist.github.com/yinjimmy/d6ad0742d03d54518e9f

import os, time, sys, subprocess

if len(sys.argv) == 2:
    time.sleep(5)
    print 'track end'
    if sys.platform == 'darwin':
        subprocess.Popen(['say', 'hello'])
else:
    print 'main begin'
    subprocess.Popen(['python', os.path.realpath(__file__), '0'], close_fds=True)
    print 'main end'

你可能想知道“如何在Python中调用外部命令”的答案。

最简单的方法是使用操作系统。系统功能,例如:

import os
os.system("some_command &")

基本上,无论您传递给系统函数的是什么,都将像在脚本中传递给shell一样执行。

两者都捕获输出并通过线程在后台运行

正如在这个答案中提到的,如果使用stdout=捕获输出,然后尝试read(),那么进程将阻塞。

然而,在某些情况下您需要这样做。例如,我想启动两个通过端口进行通信的进程,并将它们的标准输出保存到日志文件和标准输出中。

threading模块允许我们这样做。

首先,看看如何在这个问题中单独执行输出重定向部分:Python Popen:同时写入标准输出和日志文件

然后:

main.py

#!/usr/bin/env python3

import os
import subprocess
import sys
import threading

def output_reader(proc, file):
    while True:
        byte = proc.stdout.read(1)
        if byte:
            sys.stdout.buffer.write(byte)
            sys.stdout.flush()
            file.buffer.write(byte)
        else:
            break

with subprocess.Popen(['./sleep.py', '0'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) as proc1, \
     subprocess.Popen(['./sleep.py', '10'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) as proc2, \
     open('log1.log', 'w') as file1, \
     open('log2.log', 'w') as file2:
    t1 = threading.Thread(target=output_reader, args=(proc1, file1))
    t2 = threading.Thread(target=output_reader, args=(proc2, file2))
    t1.start()
    t2.start()
    t1.join()
    t2.join()

sleep.py

#!/usr/bin/env python3

import sys
import time

for i in range(4):
    print(i + int(sys.argv[1]))
    sys.stdout.flush()
    time.sleep(0.5)

在运行:

./main.py

Stdout每0.5秒更新一次,每两行包含:

0
10
1
11
2
12
3
13

每个日志文件都包含给定进程的日志。

灵感来源:https://eli.thegreenplace.net/2017/interacting-with-a-long-running-child-process-in-python/

在Ubuntu 18.04, Python 3.6.7上测试。

注:与2009年发布的答案相比,这个答案没有那么流行。现在建议在文档中使用其他答案中显示的subprocess模块

(注意,subprocess模块为生成新进程和检索其结果提供了更强大的功能;使用该模块比使用这些函数更可取。)


如果你想让你的进程在后台启动,你可以使用system()并像你的shell脚本一样调用它,或者你可以衍生它:

import os
os.spawnl(os.P_DETACH, 'some_long_running_command')

(或者,你也可以试试不那么便携的操作系统。P_NOWAIT国旗)。

请在这里查看文档。