如何终止脚本?

1394

如何早期退出脚本,就像PHP中的die()命令一样?


4
可以简单地使用import os和os._exit()。 - Amin Pial
xonsh: https://xon.sh/tutorial.html#that-s-all-folksxonsh:https://xon.sh/tutorial.html#that-s-all-folks - Andrew
14个回答

1792
import sys
sys.exit()

来自 sys 模块文档 的详细信息:

sys.exit([arg])

退出 Python。这是通过抛出SystemExit 异常来实现的,因此会执行由 try 语句中的 finally 子句指定的清理操作,并且可以在外层拦截退出尝试。

可选参数 arg 可以是一个整数,表示退出状态(默认为零),或者是另一种类型的对象。如果它是一个整数,则零被视为“正常终止”,任何非零值都被视为“异常终止”并被 shell 等程序认为是错误的。大多数系统要求它在 0-127 范围内,否则会产生未定义的结果。一些系统有将特定的退出代码分配给具体含义的约定,但这些通常不太完善;Unix 程序通常使用 2 表示命令行语法错误,使用 1 表示其他所有类型的错误。如果传递了另一种类型的对象,则 None 等价于传递零,并且会将任何其他对象打印到 stderr 并导致退出代码为 1。特别地,sys.exit("some error message") 是在发生错误时退出程序的快捷方式。

由于 exit() 最终“只”抛出异常,所以仅在从主线程调用时才能退出进程,并且该异常不会被拦截。

请注意,这是一种“优雅”的退出方式。@glyphtwistedmatrix在下面指出,如果您想实现“强制退出”,可以使用os._exit(*errorcode*),尽管它可能在某种程度上是特定于操作系统的(例如,在Windows下可能无法接受错误代码),并且它肯定不那么友好,因为它在进程死亡之前不会让解释器进行任何清理。另一方面,它确实杀死整个进程,包括所有正在运行的线程,而sys.exit()(如文档所述)仅在从主线程调用且没有其他线程运行时才会退出。


19
如果由后台线程引发,可能sys.exit()不起作用(不会终止进程,只会终止该线程)吗? - user607021
2
@cesium62:是的,sys.exit()在当前线程中引发了SystemExit异常。 - Dmitry Trofimov
20
有没有一种方法可以在不引发异常的情况下结束脚本?我已经通过将 print 输出到标准输出并通过管道传递给 Popen 来传递相关标志,因此在这种情况下,异常引发的问题比解决的问题还要多。 - Elliot
5
为什么当我使用这种方法时会收到以下警告:“UserWarning: To exit: use 'exit', 'quit', or Ctrl-D. warn("To exit: use 'exit', 'quit', or Ctrl-D.", stacklevel=1)”? - Bill
2
如果您正在使用Tkinter,它将不会停止根进程mainLoop和它所显示的窗口。为此,您需要额外添加一行root.destroy() - Roy Hinkley
显示剩余5条评论

501
使用内置的quit()函数是提前终止Python脚本的简单方法。无需导入任何库,它既高效又简单。
示例:
#do stuff
if this == that:
  quit()

131
sys.exit()将终止所有Python脚本,但quit()仅终止生成它的脚本。 - VishalDevgire
5
你知道这个命令在Python 2和Python 3中是否有不同的功能吗? - David C.
6
对我来说,它显示"quit"未定义。我正在使用Python 3。 - Vincenzooo
7
我使用的是 Python 3.7 版本,quit() 命令会停止解释器并关闭脚本。 - RockAndRoleCoder
30
根据文档,“quit()”适用于交互式解释器,不应在程序中使用。” - wovano
显示剩余5条评论

151

另一种方法是:

raise SystemExit

45
@Alessa:看起来更优雅,但不建议这样做:你直接引发了内置异常,而不是可覆盖的 sys.exit 包装器。 - MestreLion
5
这是我理想的方式:只停止运行脚本而不退出IDLE。 - rml

98
您还可以简单使用exit()
请记住,sys.exit()exit()quit()os._exit(0)终止Python解释器。因此,如果它出现在通过execfile()从另一个脚本调用的脚本中,则会停止两个脚本的执行。
请参见“停止执行使用execfile调用的脚本”以避免这种情况。

5
对我来说,os._exit(0) 是最优雅的方式。其他方法都会引发不必要的错误。 - Abdullah Ashraf
1
请勿在您的代码中使用 quit()exit()。这些函数仅适用于交互式Python。请参阅 Python退出命令-为什么有这么多,每个应该何时使用? - pabouk - Ukraine stay strong

80

虽然通常应该优先使用 sys.exit,因为它对其他代码更加“友好”,但它实际上只是引发异常。

如果您确定需要立即退出进程,并且可能在某些异常处理程序内部,这些处理程序将捕获 SystemExit,那么还有另一个函数 - os._exit - 可以在 C 级别立即终止进程,并且不执行解释器的任何正常拆卸操作;例如,“atexit”模块注册的钩子不会被执行。


1
在 AWS Glue Job 中使用 os._exit() 是唯一能够正确终止作业且不产生错误的方法。 - Jérémy
@Jérémy 但这是一个优雅的关闭吗?你知道在此之后是否会有挂起的事情吗?我是Python新手,所以请忽略我的不足之处。 - bogdan.rusu
2
@bogdan.rusu,os._exit()会在不进行任何清理的情况下退出整个进程。由于在AWS中,运行的VM实例将在作业结束时被丢弃,因此这对我来说不是问题。 - Jérémy

73
我刚刚发现,在编写多线程应用程序时,raise SystemExitsys.exit()只会终止当前运行的线程。另一方面,os._exit()会终止整个进程。这在《为什么在Python中在线程内调用sys.exit()时不退出?》中有讨论。
下面的示例有两个线程。 Kenny 和 Cartman。Cartman 应该永远存在,但是 Kenny 会被递归调用,3 秒后应该死亡。(递归调用并不是最好的方式,但我有其他原因)
如果我们还希望当 Kenny 死亡时 Cartman 也死亡,Kenny 应该使用 os._exit,否则只有 Kenny 会死亡,而 Cartman 将永远存在。
import threading
import time
import sys
import os

def kenny(num=0):
    if num > 3:
        # print("Kenny dies now...")
        # raise SystemExit #Kenny will die, but Cartman will live forever
        # sys.exit(1) #Same as above

        print("Kenny dies and also kills Cartman!")
        os._exit(1)
    while True:
        print("Kenny lives: {0}".format(num))
        time.sleep(1)
        num += 1
        kenny(num)

def cartman():
    i = 0
    while True:
        print("Cartman lives: {0}".format(i))
        i += 1
        time.sleep(1)

if __name__ == '__main__':
    daemon_kenny = threading.Thread(name='kenny', target=kenny)
    daemon_cartman = threading.Thread(name='cartman', target=cartman)
    daemon_kenny.setDaemon(True)
    daemon_cartman.setDaemon(True)

    daemon_kenny.start()
    daemon_cartman.start()
    daemon_kenny.join()
    daemon_cartman.join()

7
这似乎是处理令人讨厌的延迟回调(即多线程方法)的唯一方法! - not2qubit
5
干得好。其他答案没有直接涉及多线程,只讨论了脚本生成其他脚本的情况。 - jonspaceharper

36
from sys import exit
exit()

作为参数,您可以传递一个退出代码,该代码将返回给操作系统。默认值为0。


3
在我的情况下,甚至不需要导入 exit。 - Kostanos
13
仅为了记录上面的评论- exit()sys.exit()并不相同。在脚本中不要使用内置的exit(),这只是交互式Shell的辅助工具-请使用sys.exit() - daveruinseverything

25

我完全是个新手,但这肯定更加简洁、可控。

def main():
    try:
        Answer = 1/0
        print  Answer
    except:
        print 'Program terminated'
        return
    print 'You wont see this'

if __name__ == '__main__': 
    main()

程序已终止

import sys
def main():
    try:
        Answer = 1/0
        print  Answer
    except:
        print 'Program terminated'
        sys.exit()
    print 'You wont see this'

if __name__ == '__main__': 
    main()

程序终止 跟踪(最近的调用在最上面): 文件 "Z:\Directory\testdieprogram.py",第12行,在main()函数中 文件 "Z:\Directory\testdieprogram.py",第8行,在main()函数中 sys.exit() SystemExit

编辑

关键在于程序平稳、安静地结束,而不是"我已经停止了!!!"


26
一个问题是如果你处于嵌套函数中,只想退出当前函数,你要么需要将标志一路传回顶层函数,要么就会返回到上一层级。 - horta
18
如果你想暗示可以使用 return 来终止脚本,那么这完全是胡说八道。所有 return 所做的只是返回一个值和控制流到调用函数。然后在调用 return 的函数之后立即继续执行。当然,如果 return 是你脚本中的最后一个语句,就像你的例子一样,那么脚本会在它被调用后立即终止。 - David Ferenczy Rogožan
4
这种方法有很有力的论据支持:(1)“退出”中间环节可以被认为是一个“goto”,因此自然而然地应该避免;(2)在库中使用“退出”绝对是不好的做法(而且任何东西都可以成为库),因为一个_库_认为“不可恢复”的问题通常对_调用者_来说是可以解决的。(注意:在Python中,使用异常来退出是C/C++/Java开发者经常不适当地调用“exit”的实际解决方法--因此,Python程序员可能没有注意到这种代码气味的强烈程度);最后,(3)多线程代码(这是Python爱好者历史上一直忽略的问题)。 - michael
2
这是一种哲学上不同的方法 - OP正在询问如何“提前”停止脚本;而这个答案则是说“不要”:包含一个路径以优雅地完成,将控制返回到main方法并运行到完成。我认为在可用时应该优先采用这种方法。 - scign

18

问题

在我的实践中,有一次需要从这些进程之一中杀死整个多处理器应用程序。

如果应用程序只使用一个主进程,则以下函数运行良好。但在我的情况下,由于应用程序还有许多其他正在运行的进程,以下任何一个函数都无法正常工作。

  • quit()
  • exit(0)
  • os._exit(0)
  • sys.exit(0)
  • os.kill(os.getppid(), 9) - 其中os.getppid()是父进程的pid

最后一个函数可以杀死主进程和它本身,但是其余的进程仍然存活。

解决方案

我不得不通过外部命令杀死它,最终找到了使用pkill的解决方案。

import os

# This can be called even in process worker and will kill
# whole application included correlated processes as well
os.system(f"pkill -f {os.path.basename(__file__)}")

2
你的代码对我来说并没有起作用,但是你指引了我正确的方向:os.system("pkill -f " + sys.argv[0])。 - Oriol Vilaseca

11

在Python 3.5中,我尝试编写类似的代码,不使用模块(例如sys、Biopy)而是使用内置模块来停止脚本,并向用户打印错误消息。以下是我的示例:

## My example:
if "ATG" in my_DNA: 
    ## <Do something & proceed...>
else: 
    print("Start codon is missing! Check your DNA sequence!")
    exit() ## as most folks said above

后来,我发现只是抛出错误更加简洁:

## My example revised:
if "ATG" in my_DNA: 
    ## <Do something & proceed...>
else: 
    raise ValueError("Start codon is missing! Check your DNA sequence!")

1
我完全同意,对于应用程序可以处理的任何错误,最好引发异常。但问题是如何终止Python脚本,所以这并不是对问题的(新)答案。 - wovano

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接