繁体   English   中英

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

[英]How to execute a shell script in the background from a Python script

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

所有其他命令都可以正常运行,但是只有我要在后台运行的命令无法正常运行。

这里发生了几件事。

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

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

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

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


无论如何,在后台执行某件事的全部意义在于它始终在后台运行,而脚本始终在前台运行。 因此,您需要在returncode代码完成之前检查其returncode代码,然后继续执行代码中的下一个操作,而再也不会返回。


似乎您要等待其完成。 在这种情况下,请勿在后台运行它-使用proc.wait ,或仅使用proc.wait subprocess.call()而不是创建Popen对象。 当然,也不要使用& 当我们使用它时,请不要使用外壳程序:

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

您使用的是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".

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM