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

发布于 2024-11-07 01:48:33 字数 309 浏览 5 评论 0原文

我尝试过运行这样的事情:

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。

I've tried running things like this:

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

This works if the parent script exits gracefully, but if I kill the script (Ctrl-C), all my child processes are killed too. Is there a way to avoid this?

The platforms I care about are OS X and Linux, using Python 2.6 and Python 2.7.

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

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

发布评论

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

评论(6

梦里兽 2024-11-14 01:48:33

子进程收到与父进程相同的 SIGINT,因为它位于同一进程组中。您可以通过在子进程中调用 os.setpgrp() 来将子进程放入其自己的进程组中。 Popenpreexec_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”,但我从未尝试过。)

The child process receives the same SIGINT as your parent process because it's in the same process group. You can put the child in its own process group by calling os.setpgrp() in the child process. Popen's preexec_fn argument is useful here:

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

(preexec_fn is for un*x-oids only. There appears to be a rough equivalent for Windows "creationflags=CREATE_NEW_PROCESS_GROUP", but I've never tried it.)

坦然微笑 2024-11-14 01:48:33

在 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)

The usual way to do this on Unix systems is to fork and exit if you're the parent. Have a look at os.fork() .

Here's a function that does the job:

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)
古镇旧梦 2024-11-14 01:48:33

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

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

这是 Windows 的解决方案。

After an hour of various attempts, this works for me:

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

It's solution for windows.

花之痕靓丽 2024-11-14 01:48:33

从 3.2 开始,您还可以使用 start_new_session 标志(仅限 POSIX)。

import subprocess

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

请参阅 Popen 构造函数中的 start_new_session

Since 3.2 you can also use start_new_session flag (POSIX only).

import subprocess

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

See start_new_session in Popen constructor

红焚 2024-11-14 01:48:33

另一种方法是让子进程忽略 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

Another way is to make the subprocess ignore SIGINT.

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

Using preexec_fn ensures that the parent process's SIGINT handler is not changed. (if it's changed you need to restore it like this.)

Of course, this will only work if the subprocess does not proceed to reinstate the signal handler. In the following case where the subprocess installs a signal handler, the subprocess would still be killed:

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()

Credit to https://stackoverflow.com/a/3731948/5267751 .

夏有森光若流苏 2024-11-14 01:48:33
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 异常。

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

class subprocess.Popen(...)

Execute a child program in a new process.
On POSIX, the class uses os.execvp()-like behavior to execute the
child program. On Windows, the class uses the Windows CreateProcess()
function.

os.execvpe(file, args, env)

These functions all execute a new program, replacing the current
process; they do not return. On Unix, the new executable is loaded
into the current process, and will have the same process id as the
caller. Errors will be reported as OSError exceptions.

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