添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

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

1 人关注

我正在努力从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
john
john
发布于 2014-10-23
2 个回答
abarnert
abarnert
发布于 2014-10-23
已采纳
0 人赞同

这里有几件事情要做。

首先,你在后台启动一个shell,然后告诉那个shell在后台运行程序。我不知道你为什么认为你需要这两样东西,但我们现在先不考虑这个问题。事实上,通过在 shell=True 的基础上添加 executable='/bin/bash' ,你实际上是在 trying 来运行一个shell来运行一个shell来在后台运行程序,尽管这实际上并不完全有效。

第二,你在使用 PIPE 来表示进程的输出和错误,但又不读它们。这可能会导致子进程的死锁。如果你不想要输出,使用 DEVNULL ,而不是 PIPE 。如果你想让输出自己处理,使用 proc.communicate() .**,或者使用更高级别的函数,如 check_output 。如果你只是想让它与你自己的输出交织在一起,就不要用这些参数。

* 如果你使用shell是因为 kml_http 是一个不可执行的脚本,必须由 /bin/bash 来运行,那么就不要用 shell=True 来做这个。或者 executable ,只要让 /bin/bash 成为命令行中的第一个参数,而 /opt/kml/bin/kml_http 成为第二个参数即可。但这似乎不太可能;你为什么要把一些不可执行的东西安装到 bin 目录中?

**或者你可以从 proc.stdout proc.stderr 中明确地读出它,但那会变得更加复杂。

无论如何,在后台执行的东西的全部意义在于,它一直在后台运行,而你的脚本一直在前台运行。所以,你要在它完成之前检查它的 returncode ,然后继续进行你的代码中的下一个内容,并且不再回来。

看起来你想等它完成。在这种情况下,不要在后台运行它--使用 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.

现在,在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
    
Mark Nunberg
Mark Nunberg
发布于 2014-10-23
0 人赞同

你使用了 stderr=PIPE, stdout=PIPE ,这意味着不是让子进程的 stdin stdout 被转发到当前进程的标准输出和错误流。它们被重定向到一个管道,你必须在你的 python 进程中读取这个管道(通过 proc.stdout proc.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)