【问题标题】:How to execute a shell script in the background from a Python script如何从 Python 脚本在后台执行 shell 脚本
【发布时间】:2014-12-18 14:52:58
【问题描述】:

我正在从 Python 执行 shell 脚本,到目前为止它运行良好。但我被困在一件事上。

在我的 Unix 机器上,我使用 & 像这样在后台执行一个命令。此命令将启动我的应用服务器 -

david@machineA:/opt/kml$ /opt/kml/bin/kml_http --config=/opt/kml/config/httpd.conf.dev &

现在我需要从我的 Python 脚本中执行相同的操作,但是一旦它执行我的命令,它就永远不会转到 else block 并且永远不会打印出 execute_steps::Successful,它只是挂在那里。

proc = subprocess.Popen("/opt/kml/bin/kml_http --config=/opt/kml/config/httpd.conf.dev &", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, executable='/bin/bash')
if proc.returncode != 0:
    logger.error("execute_steps::Errors while executing the shell script: %s" % stderr)
    sleep(0.05) # delay for 50 ms
else:
    logger.info("execute_steps::Successful: %s" % stdout)

我在这里做错什么了吗?我想在后台执行shell脚本后打印出execute_steps::Successful

所有其他命令都可以正常工作,但只有我试图在后台运行的命令不能正常工作。

【问题讨论】:

    标签: python linux shell subprocess


    【解决方案1】:

    您正在使用stderr=PIPE, stdout=PIPE,这意味着不是让子进程的stdinstdout 被转发到当前进程的标准输出和错误流,而是将它们重定向到您的管道必须从您的 python 进程中读取(通过proc.stdoutproc.stderr

    要“后台”一个进程,只需省略PIPE的用法:

    #!/usr/bin/python
    from subprocess import Popen
    from time import sleep
    
    proc = Popen(
        ['/bin/bash', '-c', 'for i in {0..10}; do echo "BASH: $i"; sleep 1; done'])
    
    for x in range(10):
        print "PYTHON: {0}".format(x)
        sleep(1)
    
    proc.wait()
    
    which will show the process being "backgrounded".
    

    【讨论】:

      【解决方案2】:

      这里发生了一些事情。

      首先,您在后台启动一个 shell,然后告诉该 shell 在后台运行程序。我不知道你为什么认为你需要两者,但现在让我们忽略它。事实上,通过在shell=True 之上添加executable='/bin/bash',您实际上是在试图运行一个shell 来运行一个shell 以在后台运行程序,尽管这实际上并不完全工作。*

      其次,您将PIPE 用于进程的输出和错误,但随后不读取它们。这可能会导致孩子陷入僵局。如果您不想要输出,请使用DEVNULL,而不是PIPE。如果您希望自己处理输出,请使用proc.communicate().**,或使用更高级别的函数,如check_output。如果您只是希望它与您自己的输出混合,请不要使用这些参数。

      * 如果因为kml_http 是必须由/bin/bash 运行的不可执行脚本而使用shell,则不要为此使用shell=True,或executable,只需将/bin/bash 设为命令行中的第一个参数,将/opt/kml/bin/kml_http 设为第二个。但这似乎不太可能;为什么要将不可执行的东西安装到bin 目录中?

      ** 或者您可以从proc.stdoutproc.stderr 中明确读取它,但这会变得更加复杂。


      无论如何,在后台执行某事的全部意义在于它一直在后台运行,而您的脚本一直在前台运行。所以,你在它完成之前检查它的returncode,然后继续你代码中的下一个,永远不会再回来。


      您似乎想等待它完成。在这种情况下,不要在后台运行它——使用proc.wait,或者只使用subprocess.call() 而不是创建Popen 对象。当然也不要使用&。当我们这样做的时候,也不要使用 shell:

      retcode = subprocess.call(["/opt/kml/bin/kml_http",
                                 "--config=/opt/kml/config/httpd.conf.dev"],
                                stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
      if retcode != 0:
          # etc.
      

      现在,在 kml_http 运行完成之前,您将无法访问该 if 语句。


      如果你想等待它完成,但同时继续做其他事情,那么你试图在你的程序中同时做两件事,这意味着你需要一个线程来做等待:

      def run_kml_http():
          retcode = subprocess.call(["/opt/kml/bin/kml_http",
                                     "--config=/opt/kml/config/httpd.conf.dev"],
                                    stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
          if retcode != 0:
              # etc.
      
      t = threading.Thread(target=run_kml_http)
      t.start()
      # Now you can do other stuff in the main thread, and the background thread will
      # wait around until kml_http is finished and execute the `if` statement whenever
      # that happens
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-05-21
        • 2019-12-11
        • 1970-01-01
        • 1970-01-01
        • 2023-03-23
        • 1970-01-01
        • 2019-03-16
        • 2011-12-01
        相关资源
        最近更新 更多