从python运行一个程序,并在脚本被杀死后让它继续运行

问题描述 投票:0回答:6

我尝试过运行这样的东西:

subprocess.Popen(['nohup', 'my_command'],
                 stdout=open('/dev/null', 'w'),
                 stderr=open('logfile.log', 'a'))

如果父脚本正常退出,则此方法有效,但如果我终止脚本(Ctrl-C),我的所有子进程也会被终止。有没有办法避免这种情况?

我关心的平台是 OS X 和 Linux,使用 Python 2.6 Python 2.7。

python subprocess nohup
6个回答
66
投票

子进程收到与父进程相同的

SIGINT
,因为它位于同一进程组中。您可以通过在子进程中调用
os.setpgrp()
将子进程放入其自己的进程组中。
Popen
preexec_fn
参数在这里很有用:

subprocess.Popen(['nohup', 'my_command'],
                 stdout=open('/dev/null', 'w'),
                 stderr=open('logfile.log', 'a'),
                 preexec_fn=os.setpgrp
                 )

preexec_fn
仅适用于un*x-oids。Windows似乎有一个粗略的等效项“
creationflags=CREATE_NEW_PROCESS_GROUP
”,但我从未尝试过。)


32
投票

在 Unix 系统上执行此操作的常用方法是 fork 并退出(如果您是父级)。看看

os.fork()

这是一个可以完成这项工作的函数:

def spawnDaemon(func):
    # do the UNIX double-fork magic, see Stevens' "Advanced 
    # Programming in the UNIX Environment" for details (ISBN 0201563177)
    try: 
        pid = os.fork() 
        if pid > 0:
            # parent process, return and keep running
            return
    except OSError, e:
        print >>sys.stderr, "fork #1 failed: %d (%s)" % (e.errno, e.strerror) 
        sys.exit(1)

    os.setsid()

    # do second fork
    try: 
        pid = os.fork() 
        if pid > 0:
            # exit from second parent
            sys.exit(0) 
    except OSError, e: 
        print >>sys.stderr, "fork #2 failed: %d (%s)" % (e.errno, e.strerror) 
        sys.exit(1)

    # do stuff
    func()

    # all done
    os._exit(os.EX_OK)

16
投票

经过一小时的各种尝试,这对我有用:

process = subprocess.Popen(["someprocess"], creationflags=subprocess.DETACHED_PROCESS | subprocess.CREATE_NEW_PROCESS_GROUP)

这是 Windows 的解决方案。


4
投票

从 3.2 开始,您还可以使用

start_new_session
标志(仅限 POSIX)。

import subprocess

p = subprocess.Popen(["sleep", "60"], start_new_session=True)
ret = p.wait()

参见 Popen 构造函数中的 start_new_session


0
投票

另一种方法是让子进程忽略SIGINT。

import subprocess
import signal
subprocess.Popen(["sleep", "100"], preexec_fn=lambda: signal.signal(signal.SIGINT, signal.SIG_IGN))

使用

preexec_fn
确保父进程的 SIGINT 处理程序不会更改。 (如果更改了,您需要恢复它像这样。)

当然,只有当子进程不继续恢复信号处理程序时,这才有效。在以下子进程安装信号处理程序的情况下,子进程仍然会被杀死:

import subprocess
import signal
process=subprocess.Popen(["python", "-c", "import signal\nimport time\nsignal.signal(signal.SIGINT, signal.SIG_DFL)\nwhile True: \n    print(1)\n    time.sleep(1)"], preexec_fn=lambda: signal.signal(signal.SIGINT, signal.SIG_IGN))
process.wait()

感谢 https://stackoverflow.com/a/3731948/5267751 .


-1
投票
with open('/dev/null', 'w') as stdout, open('logfile.log', 'a') as stderr:
    subprocess.Popen(['my', 'command'], stdout=stdout, stderr=stderr)

类 subprocess.Popen(...)

在新进程中执行子程序。 在 POSIX 上,该类使用类似 os.execvp() 的行为来执行 儿童计划。在 Windows 上,该类使用 Windows CreateProcess() 功能。

os.execvpe(文件,参数,环境)

这些函数都执行一个新的程序,替换当前的程序 过程;他们没有回来。在 Unix 上,加载新的可执行文件 进入当前进程,并且将具有与当前进程相同的进程ID 呼叫者。错误将被报告为 OSError 异常。

© www.soinside.com 2019 - 2024. All rights reserved.