如何从Python脚本在后台执行shell脚本

时间:2014-10-23 00:56:27

标签: python linux shell subprocess

我正在从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

所有其他命令工作正常但只有我尝试在后台运行的命令不能正常工作。

2 个答案:

答案 0 :(得分:1)

这里发生了一些事情。

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

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

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

**或者您可以从binproc.stdout明确地阅读它,但这会变得更加复杂。


无论如何,在后台执行某些操作的重点是它始终在后台运行,并且您的脚本在前台运行。因此,您需要在proc.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.

现在,在if完成运行之前,您无法使用kml_http语句。


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

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

答案 1 :(得分:0)

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

To" background"一个过程,只是省略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".