使用python中的子进程进行错误检查的管道

时间:2013-07-31 20:24:07

标签: python unix subprocess pipe

我有一个使用subprocess的管道方案,其中一个进程p2将另一个进程p1的输出作为输入:

p1 = subprocess.Popen("ls -al", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
p2 = subprocess.Popen("grep mytext - ", shell=True, stdin=p1.stdout, stdout=subprocess.PIPE)
result = p2.communicate()

p1p2可能由于各种原因而失败,例如错误输入或格式错误的命令。

p1没有失败时,此代码可以正常工作。如何执行此操作,还要检查p1具体或p2是否明确失败?例如:

# p1 will fail since notafile does not exist
p1 = subprocess.Popen("ls notafile", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
p2 = subprocess.Popen("grep mytext - ", shell=True, stdin=p1.stdout, stdout=subprocess.PIPE)
result = p2.communicate()

我可以查看p2.returncode并查看它不是0,但这可能意味着p2失败或p1失败。如何在管道出错的情况下专门检查p1是否失败或p2失败?

我不知道如何使用p1.returncode这是理想而明显的解决方案。例如:

p1 = subprocess.Popen("ls foo", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
p2 = subprocess.Popen("grep mytext - ", shell=True, stdin=p1.stdout, stdout=subprocess.PIPE)
# here, p2.returncode not defined yet since we didn't communicate()
assert(p2 is None)
r = p2.communicate()
# now p2 has a returncode
assert(p2.returncode is not None)
# ... but p1 does not!
assert(p1.returncode is None)

所以我不知道返回码在这里有什么帮助吗?

感谢@abarnert的完整解决方案是这样的:

p1 = subprocess.Popen("ls -al", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
p2 = subprocess.Popen("grep mytext - ", shell=True, stdin=p1.stdout, stdout=subprocess.PIPE)
result = p2.communicate()
if p2.returncode != 0:
  # something failed
  if p1.wait() != 0:
     # p1 failed...
  else: 
     # p2 failed...

P.S。为了安全起见,我知道shell=True的警告。

1 个答案:

答案 0 :(得分:2)

  

我可以查看result.returncode并查看它不是0,但这可能意味着p2失败。

不,你不能; result只是一个元组(stdoutdata, stderrdata)

具有Popen值的returncode个对象。

这就是你的答案:

  

如果此管道出错,我该如何专门检查p1是否失败或p2是否失败?

只需检查p1.returncode

但是,请注意虽然p2.communicate 确实保证p2wait,但它保证同样适用于p1。幸运的是,它应该保证p1至少wait能够,wait不能if p2.returncode: if p1.wait(): # p2 failed, probably because p1 failed else: # p2 failed for some other reason ,所以:

p1.stdout.close()

除了你几乎肯定想在communicate之前做p1.wait()。否则,进程2中的错误可能会导致进程1被阻止,因此p1.poll()可能会永久阻塞。 (你可以在这里使用p1.stderr解决这个问题,如果还没有完成就杀掉它,但实际上,最好不要创建问题而不是解决问题。)

最后一件事:您已将subprocess.PIPE设置为{{1}},它永远不会附加到任何内容,因此进程1也可能阻止尝试写入溢出的stderr管道。您可能也希望解决这个问题。