subprocess.Popen的奇怪执行模式

时间:2017-10-09 13:26:15

标签: java python bash shell

我有一个Python脚本,其中调用了JAR。调用JAR后,将调用两个shell脚本。最初我这样做:

proc = subprocess.Popen(jar_command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
proc.wait()
output, errors = proc.communicate()

proc = subprocess.Popen(prune_command, shell=True)
proc.wait()

proc = subprocess.call(push_command, shell=True)

我必须等待前两个进程完成,所以我使用Popen(),最后一个我可以让它在后台运行,所以我call()。我传递shell=True因为我希望被调用的shell脚本能够访问环境变量。

但是,上面的工作,我没有从JAR进程中获取任何日志记录。我试过这样称呼它:

proc = subprocess.call(jar_command)

这会按照我的预期记录,但后面的两个shell脚本不会被执行。最初我认为日志不是stdout,但事实证明它们根本没有被执行。 I.E.不删除多余的文件或推送到数据库。

为什么要忽略后续shell脚本?

2 个答案:

答案 0 :(得分:3)

如果您某些您的shell脚本根本没有运行,并且第一个代码一切正常 - 那么它必须 java命令死锁或不正确终止使用call()函数。

您可以通过在bash脚本中添加虚拟文件来验证这一点。把它放在脚本的第一行,所以如果它被执行,你将获得创建的虚拟文件。如果它没有被创建,那意味着脚本没有被执行,可能是因为java执行的东西。

我会尝试几件事:

首先,我会返回Popen而不是call。不要使用wait(),而是使用communicate()

  

与流程交互:将数据发送到stdin。从stdout和stderr读取数据,直到达到文件结尾。 等待进程终止。    communic()返回元组(stdoutdata,stderrdata)

proc = subprocess.Popen(jar_command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
proc.communicate()

确保检查两个数据流(stdout和stderr)。您可能会错过java进程引发的错误。

接下来,我会尝试通过向bufsize=0提供Popen来禁用缓冲区。它将消除与python缓冲相关的选项。

如果两个选项仍然不起作用,请尝试使用check_call()查看是否存在例外:

proc = subprocess.check_call(jar_command)
  

使用参数运行命令。等待命令完成。如果返回代码为零则返回,否则提升 CalledProcessError

这些选项可能有答案;如果没有,他们会帮助调试过程。随意评论这一进展如何。

答案 1 :(得分:0)

最有可能的是,您忘记了进程流实际上是具有一定容量的OS级缓冲区。

例如,如果你运行一个在PIPE模式下产生大量输出的进程,并且在尝试使用该进程写入输出的任何内容之前等待它完成,那么就会出现死锁:

  • 该进程已填满输出缓冲区,现在阻止将更多数据写入其输出。在有人通过读取管道清空缓冲区之前,该过程无法继续。
  • 在从缓冲区读取数据之前,您的程序正在等待子进程完成。

正确的方法是在程序中启动一个线程,以便排出"排除"当进程运行并且主线程正在等待时,管道会不断进行。您必须先启动流程,然后启动排水螺纹,然后等待流程完成。

对于差分诊断,检查子进程是否运行良好且输出很少(即只要缓冲区没有填满,例如一行或两行)。

子流程的文档有关于此的note