58

我有一个基于 python 的 GTK 应用程序,它加载了几个模块。它从(linux)终端运行,如下所示:

./myscript.py --some-flag setting

用户可以从程序中下载(使用 Git)较新的版本。如果存在/已下载,则会出现一个按钮,我希望使用新编译的内容(包括依赖项/导入)重新启动程序。最好它还使用 的内容重新启动它,sys.argv以保持所有标志不变。

所以我找不到/需要的是一个很好的重启过程,它会杀死程序的当前实例并使用相同的参数启动一个新实例。

该解决方案最好也适用于 Windows 和 Mac,但这不是必需的。

4

14 回答 14

67

您正在寻找os.exec*()命令系列。

要使用与最初运行的命令行参数完全相同的命令行参数重新启动当前程序,可以使用以下命令:

os.execv(sys.argv[0], sys.argv)
于 2012-07-04T13:22:16.723 回答
39

我认为这是一个更详细的答案,因为有时您最终可能会打开太多打开的文件对象和描述符,这可能会导致内存问题或与网络设备的并发连接。

import os
import sys
import psutil
import logging

def restart_program():
    """Restarts the current program, with file objects and descriptors
       cleanup
    """

    try:
        p = psutil.Process(os.getpid())
        for handler in p.get_open_files() + p.connections():
            os.close(handler.fd)
    except Exception, e:
        logging.error(e)

    python = sys.executable
    os.execl(python, python, *sys.argv)
于 2015-10-25T19:37:03.293 回答
5

更新 - 以上答案的一些示例以供将来参考

我有runme.sh

#!/bin/bash
kill -9 server.py
python /home/sun/workspace/c/src/server.py &

我有server.py我需要重新启动应用程序本身的地方,所以我有:

os.system('runme.sh')

但这并没有通过遵循runme.sh重新启动应用程序本身,所以当我使用这种方式时:

os.execl('runme.sh', '')

然后我能够重新启动自己

于 2013-09-22T15:43:29.600 回答
4

我知道这个解决方案在技术上并不是你所要求的,但如果你想 100% 确定你释放了所有东西或者不想依赖依赖项,你可以从另一个循环中运行脚本:

import os, time

while 1:
    os.system("python main.py")
    print "Restarting..."
    time.sleep(0.2) # 200ms to CTR+C twice

然后你可以重启 main.py,就像这样:

quit()
于 2018-02-02T06:54:19.893 回答
3

对我来说,这部分就像一个魅力:

def restart():
    import sys
    print("argv was",sys.argv)
    print("sys.executable was", sys.executable)
    print("restart now")

    import os
    os.execv(sys.executable, ['python'] + sys.argv)

我在这里得到它。

于 2021-03-01T20:06:02.167 回答
2

在 Windows 上工作(没有参数)

os.startfile(__file__)
sys.exit()

或者

os.startfile(sys.argv[0])
sys.exit()
于 2019-02-27T21:12:40.357 回答
1

我对#s3niOr 的代码做了一点改进。

在我的情况下,python文件的路径中有空格。因此,通过添加适当的格式,可以解决问题。

请注意,在我的情况下,我的 python 文件没有其他参数。因此,如果您确实有其他论点,则必须处理它们。

这解决了重新启动路径中有空格的 python 脚本的问题:

import os
import sys
import psutil
import logging

def restart_program():
    """Restarts the current program, with file objects and descriptors
       cleanup
    """

    try:
        p = psutil.Process(os.getpid())
        for handler in p.get_open_files() + p.connections():
            os.close(handler.fd)
    except Exception, e:
        logging.error(e)

    python = sys.executable
    os.execl(python, python, "\"{}\"".format(sys.argv[0]))
于 2019-01-30T16:46:30.327 回答
1

我一直在寻找解决方案,但没有发现任何适用于任何堆栈溢出帖子的内容。也许有些答案太过时了,例如 os.system 已被 subprocess 取代。我在 linux lubuntu 17.10 上,使用 python 3。

两种方法对我有用。两者都打开一个新的 shell 窗口并在其中运行 main.py 脚本,然后关闭旧的。

1. 使用带有 .sh 脚本的 main.py。

改编自@YumYumYum 方法。我不需要 kill 选项,(尽管它并没有按名称杀死我的 python 进程,我必须killall python3在测试时使用它来实现它)。

我使用 lxterminal,但您可能可以使用任何一个。

在名为 restart.sh 的文件中(chmod +x 使其可执行)

#!/bin/bash

lxterminal -e python3 /home/my/folder/main.py &

然后在 main.py 中,当你需要调用它时使用它

import subprocess

subprocess.run('/home/my/folder/restart.sh', shell=True)
quit()

2. 从 main.py 中

改编自 @Stuffe 方法。这个在 main.py 脚本内部,打开一个新的 shell 窗口,然后运行新脚本,然后退出旧脚本。我不确定它是否需要 time.sleep 延迟,但我还是使用了它。

import subprocess, time

cmd = 'python3 /home/my/folder/main.py'
subprocess.run('lxterminal -e ' + cmd, shell=True)   
time.sleep(0.2)
quit()
于 2019-10-23T08:55:56.200 回答
0

受@YumYumYum 启发并使用restart.sh 和os.execl 解决了问题

restart.sh

#!/bin/bash/
pkill -f main.py
python main.py

将此添加到您的 main.py

os.excel("restart.sh","")
于 2018-06-11T07:57:31.803 回答
0

我正在使用它为用户提供在控制台中重新启动脚本的选项。希望能有所帮助。

def close_restart(self,csvfile):

    choice = input('Do you want to restart the program? Please select \'Y\' if you would like to restart.')

    if choice == 'Y' or choice == 'y':
        print('Restarting now...')
        os.execl(sys.executable, sys.executable, *sys.argv)

    else:
        print('Thank you for using the tool!')
        print('The program will close in 10s...')
        time.sleep(10)

所以用户可以输入一个选项'Y/N'来重新启动程序。

于 2019-07-15T02:18:51.360 回答
0

旧的答案利用exec这很好,但从长远来看是不可扩展的。还有一种主/从进程关系或后台守护程序/服务的方法,它们将监视更改,但主要是特定于操作系统的,甚至在同一操作系统系列(init.d vs systemd vs)之间也不同。

通过使用引导技术和简单subprocess.Popen()调用也有一个中间立场,因此假设启动原始程序的用户有权运行可执行文件(例如/usr/bin/python),由于使用完全相同的可执行文件,也应该在没有任何权限错误的情况下工作。引导,因为它是创建和调用重启程序的主程序,也就是在初始启动后通过自己的引导程序拉动自己。

所以一个简单的程序(重新)启动器可以这样写,如其他答案中所写:

from subprocess import Popen
from time import sleep

def main():
    sleep(<delay>)
    Popen([<executable path>, *<list of argv>], cwd=<cwd path>)

if __name__ == "__main__":
    main()

根据您的需要,您可能希望在之后进行一些清理,例如删除(重新)启动文件。

import sys
from os import remove
from os.path import realpath
from subprocess import Popen
from time import sleep

def start():
    sleep(<delay>)
    # unpack -----------------v
    Popen([<executable path>, *<list of argv>], cwd=<cwd path>)

def cleanup():
    remove(realpath(sys.argv[0]))

def main():
    start()
    cleanup()

if __name__ == "__main__":
    main()

该文件将从您的主程序中调用。但是,您的主程序可能有异常,利用sys.exit(),可能被操作系统信号杀死等等。Python 提供了多个钩子,如何在这样的事件之后无缝地做某事,其中一个是由atexit模块实现的。atexit也不关心 Python 异常和一些信号 ( SIGINT) (用于进一步改进检查signal模块),因此在实现自己的信号处理程序之前这是一个合理的选择。

这允许您注册一个将在程序停止后执行的函数。该函数可以是 Python 中的任何内容,因此它也可以写入文件。

文件内容本身可以使用 F 字符串、format().、Jinja 进行模板化,甚至可以将其保留在主程序之外 ( restartable.py),甚至可以通过 CLI + 提供值,argparse例如python restarter.py --exe <path> --argv <one> [--argv <two>, ...], --cwd <cwd>. 一切都取决于用例以及在实现操作系统服务/守护程序或主/从进程生成+监视之前您希望将其扩展多远。

这是一个示例:

# restartable.py
import sys
from atexit import register

# getcwd() is necessary if you want to prevent issues
# with implicitly changing working directory by a mistake
from os import getpid, getcwd
from os.path import exists, realpath, join, dirname
from subprocess import Popen
from tempfile import NamedTemporaryFile

RESTARTER = """
import sys
from atexit import register
from os import remove
from os.path import realpath
from subprocess import Popen
from time import sleep

def start():
    # unnecessary, but may provide enough breathing space
    # for the previous process to shut down correctly
    # alternatively, watch for a file/file contents
    # or utilize a socket
    sleep({delay})
    # repr() on strings, list is passed as is to unpack it properly
    # will fail on custom objects / serialization
    Popen([{exe!r}, *{argv}], cwd={cwd!r})

def cleanup():
    # remove itself because it's always created
    # by the main program on exit
    remove(realpath(sys.argv[0]))

def main():
    # the register() call can be encapsulated in a condition
    # so it restarts only in some cases
    register(cleanup)
    start()

if __name__ == "__main__":
    main()
""".lstrip("\n")

def restart():
    with NamedTemporaryFile(mode="w", delete=False) as file:
        file.write(RESTARTER.format(
            delay=5,  # 5s until restart
            exe=sys.executable,
            argv=sys.argv,
            cwd=getcwd()
        ))

        # call the restarting program by the Python executable
        # which started the main program
        Popen([sys.executable, file.name])

def main():
    # create a "norestart.txt" in the folder of "restartable.py" to halt
    if not exists(join(dirname(realpath(__file__)), "norestart.txt")):
        register(restart)

    # tail -f log.txt to check it works properly
    # or "ps aux|grep python"
    with open("log.txt", "a") as file:
        file.write(f"Hello, from {getpid()}\n")


if __name__ == "__main__":
    main()

注意:使用临时文件夹可能会失败,因此在这种情况下,只需将其切换到join(dirname(realpath(__file__)), "restarter.py")并从主程序中调用它。

于 2021-09-24T18:56:41.033 回答
0
os.execv(sys.executable, ['python3'] + sys.argv)

或者

os.execv(sys.executable, ['python2'] + sys.argv)
于 2021-10-01T15:11:00.213 回答
0

在 Windows 上尝试这项工作:

当您想重新启动脚本时调用此函数

import os
def rest():
    os.system('cls')
    script_name = os.path.basename(__file__)
    os.system(script_name)
于 2022-02-26T09:38:50.153 回答
-1

我这样做如下:

    def restartApp(self):
        python = sys.executable
        os.execl(python, python, *sys.argv)

奇迹般有效

于 2022-02-05T13:04:00.193 回答