Python 子进程:cmd 退出时的回调

新手上路,请多包涵

我目前正在使用 subprocess.Popen(cmd, shell=TRUE) 启动程序

我是 Python 的新手,但它“感觉”应该有一些 api 可以让我做类似的事情:

 subprocess.Popen(cmd, shell=TRUE,  postexec_fn=function_to_call_on_exit)

我这样做是为了 function_to_call_on_exit 可以在知道 cmd 已经退出的情况下做一些事情(例如保持当前运行的外部进程的数量)

我假设我可以相当简单地将子进程包装在一个类中,该类将线程与 Popen.wait() 方法结合在一起,但是因为我还没有在 Python 中完成线程,而且看起来这对于 API 来说可能足够普遍存在,我想我会先尝试找到一个。

提前致谢 :)

原文由 Who 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 474
2 个回答

你是对的 - 没有好的 API 可以做到这一点。您的第二点也是正确的-设计一个使用线程为您执行此操作的函数非常容易。

 import threading
import subprocess

def popen_and_call(on_exit, popen_args):
    """
    Runs the given args in a subprocess.Popen, and then calls the function
    on_exit when the subprocess completes.
    on_exit is a callable object, and popen_args is a list/tuple of args that
    would give to subprocess.Popen.
    """
    def run_in_thread(on_exit, popen_args):
        proc = subprocess.Popen(*popen_args)
        proc.wait()
        on_exit()
        return
    thread = threading.Thread(target=run_in_thread, args=(on_exit, popen_args))
    thread.start()
    # returns immediately after the thread starts
    return thread

即使线程在 Python 中也很容易,但请注意,如果 on_exit() 的计算量很大,您将希望将其放在一个单独的进程中,而不是使用多进程(这样 GIL 就不会减慢您的程序)。它实际上非常简单 - 您基本上可以将对 — 的所有调用替换为 threading.Thread multiprocessing.Process 因为它们遵循(几乎)相同的 API。

原文由 Daniel G 发布,翻译遵循 CC BY-SA 4.0 许可协议

Python 3.2 中有 concurrent.futures 模块(可通过 pip install futures 获取旧版 Python < 3.2):

 pool = Pool(max_workers=1)
f = pool.submit(subprocess.call, "sleep 2; echo done", shell=True)
f.add_done_callback(callback)

回调将在调用 f.add_done_callback() 的同一进程中调用。

完整程序

import logging
import subprocess
# to install run `pip install futures` on Python <3.2
from concurrent.futures import ThreadPoolExecutor as Pool

info = logging.getLogger(__name__).info

def callback(future):
    if future.exception() is not None:
        info("got exception: %s" % future.exception())
    else:
        info("process returned %d" % future.result())

def main():
    logging.basicConfig(
        level=logging.INFO,
        format=("%(relativeCreated)04d %(process)05d %(threadName)-10s "
                "%(levelname)-5s %(msg)s"))

    # wait for the process completion asynchronously
    info("begin waiting")
    pool = Pool(max_workers=1)
    f = pool.submit(subprocess.call, "sleep 2; echo done", shell=True)
    f.add_done_callback(callback)
    pool.shutdown(wait=False) # no .submit() calls after that point
    info("continue waiting asynchronously")

if __name__=="__main__":
    main()

输出

$ python . && python3 .
0013 05382 MainThread INFO  begin waiting
0021 05382 MainThread INFO  continue waiting asynchronously
done
2025 05382 Thread-1   INFO  process returned 0
0007 05402 MainThread INFO  begin waiting
0014 05402 MainThread INFO  continue waiting asynchronously
done
2018 05402 Thread-1   INFO  process returned 0

原文由 jfs 发布,翻译遵循 CC BY-SA 2.5 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题