【问题标题】:piping with error checking using subprocess in python在python中使用子进程进行错误检查的管道
【发布时间】:2013-08-01 13:27:53
【问题描述】:

我有一个使用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 没有失败时,此代码可以正常工作。我该如何做到这一点,还要检查 p1p2 是否特别失败?示例:

# 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)

所以我看不到 returncode 在这里有什么帮助?

感谢@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...

附言我知道shell=True 的安全警告。

【问题讨论】:

  • 暂时忘记shell=True 的安全警告;它也使事情变得更加复杂,因为您实际上根本没有将ls 传递给grep;您正在将一个sh 传递给另一个sh。你这样做有什么原因吗?
  • 在我的应用程序中lsgrep 是更有趣的命令行程序(它们在文件输入等中使用shell 功能,因此需要shell=True)。上述方法有什么问题?我将 ls 的输出从 shell 传送到 grep,从而实现了 grep ls 输出的正确结果
  • 另外,result 没有returncode;是p1p2
  • @abarnet:是的,这是错字,已修复
  • 还有几个问题。 (1) 调用 p1.wait() 即使 p2 成功,因为这会从进程表中清除僵尸进程。 (2) 如果 p1 向 stderr 写入过多,它将挂起,因为您没有阅读它。查看 Popen.communicate 代码,看看它是如何创建 theads 来读取管道并自己为 p1.stderr 执行此操作的。

标签: python unix subprocess pipe


【解决方案1】:

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

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

具有returncode 值的是Popen 对象。

这就是你的答案:

在这个管道出错的情况下,如何具体检查 p1 失败或 p2 失败?

只需检查p1.returncode

但是,请注意,虽然p2.communicate 确实保证p2 已被waited on,但它确实保证p1 相同.幸运的是,它应该保证p1至少是waitable,而不是waited,所以:

if p2.returncode:
    if p1.wait():
        # p2 failed, probably because p1 failed
    else:
        # p2 failed for some other reason

除了你几乎肯定想在communicate 之前做一个p1.stdout.close()。否则,进程 2 中的错误可能会导致进程 1 被阻塞,因此您的 p1.wait() 可能会永远阻塞。 (您可以通过在此处使用p1.poll() 来解决此问题,如果未完成则将其杀死,但实际上,最好不要产生问题而不是解决它。)

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

【讨论】:

  • 返回码检查将是理想的解决方案,但我不知道它是如何工作的,我编辑了我的答案以反映问题。有什么想法吗?也许我误解了你的答案
  • @user248237dfsf:我的回答已经说明了这一点。你真的尝试过使用p1.wait() 的代码吗?
  • @abarnet:我不明白还是很抱歉——如果我使用 wait(),我会得到相同的结果。我设置 p1,p2 然后执行:p2.wait() 这使得 p1.returncode 成为 None 而不是返回码值
  • @user248237dfsf:你为什么希望p2.wait() 做任何事情?进程 2 已经被wait 编辑为communicate,并且您已经有了它的返回码。过程 1 可能还没有收割。这就是为什么我在答案和评论中都写了p1.wait()
  • @user248237dfsf:创建 p1 时,您有 stderr=subprocess.PIPE。这将创建一个 new 管道(不同于为 stdout=subprocess.PIPE 创建的管道)。如果管道“填满”,某人或某事必须在某个时候读取 stderr 数据。如果没有人读取它,您可以丢弃数据,但前提是管道没有完全填满。您可能根本不想设置 p1 的 stderr,或者可能通过 stdout 管道发送它(在 sh 中是 foo 2>&1 | bar)。要执行后者,请使用stderr=subprocess.STDOUT
猜你喜欢
  • 2019-06-10
  • 1970-01-01
  • 2019-04-12
  • 2013-02-17
  • 2011-05-05
  • 1970-01-01
  • 2017-02-18
  • 2011-04-05
  • 2016-06-05
相关资源
最近更新 更多