运行 Python 程序,并让其在脚本终止后继续运行
- 2025-02-11 09:51:00
- admin 原创
- 74
问题描述:
我尝试过运行这样的事情:
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。
解决方案 1:
子进程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
”,但我从来没试过。)
解决方案 2:
在 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)
解决方案 3:
经过一个小时的各种尝试后,这对我来说是有效的:
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
解决方案 5:
另一种方法是让子进程忽略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
import time
signal.signal(signal.SIGINT, signal.SIG_DFL)
while True:
print(1)
time.sleep(1)"], preexec_fn=lambda: signal.signal(signal.SIGINT, signal.SIG_IGN))
process.wait()
感谢https://stackoverflow.com/a/3731948/5267751。
解决方案 6:
with open('/dev/null', 'w') as stdout, open('logfile.log', 'a') as stderr:
subprocess.Popen(['my', 'command'], stdout=stdout, stderr=stderr)
类子进程.Popen(...)
在新进程中执行子程序。在 POSIX 上,该类使用类似 os.execvp() 的行为来执行子程序。在 Windows 上,该类使用 Windows CreateProcess() 函数。
os.execvpe(文件,参数,环境)
这些函数都会执行一个新程序,替换当前进程;它们不会返回。在 Unix 上,新的可执行文件将加载到当前进程中,并且将具有与调用者相同的进程 ID。错误将报告为 OSError 异常。