【问题标题】:Why does shell=True work when piping commands together?为什么当管道命令在一起时 shell=True 工作?
【发布时间】:2013-03-12 23:18:29
【问题描述】:

我有几个subprocess 实例,我想将它们串在一起形成一个管道,但我被困住了,想征求意见。

例如,模仿:

cat data | foo - | bar - > result

或者:

foo - < data | bar - > result

...我首先尝试了以下,它挂起:

import subprocess, sys

firstProcess = subprocess.Popen(['foo', '-'], stdin=subprocess.PIPE,
                                stdout=subprocess.PIPE)
secondProcess = subprocess.Popen(['bar', '-'], stdin=firstProcess.stdout,
                                 stdout=sys.stdout)

for line in sys.stdin:
    firstProcess.stdin.write(line)
    firstProcess.stdin.flush()

firstProcess.stdin.close()
firstProcess.wait()

我的第二次尝试使用带有shell=True 参数的subprocess 实例,该实例有效:

import subprocess, sys

pipedProcess = subprocess.Popen(" ".join(['foo', '-', '|', 'bar', '-']),
                                stdin=subprocess.PIPE, shell=True)

for line in sys.stdin:
    pipedProcess.stdin.write(line)
    pipedProcess.stdin.flush()

pipedProcess.stdin.close()
pipedProcess.wait()

第一个链式subprocess 方法我做错了什么?我读到最好不要使用shell=True,我很好奇我在第一种方法中做错了什么。谢谢你的建议。

编辑

我修正了我的问题中的一个错字并修正了secondProcessstdin 参数。它仍然挂起。

我还尝试删除解决挂起的firstProcess.wait(),但随后我得到一个 0 字节文件为result

我会坚持使用pipedProcess,因为它可以正常工作。但是,如果有人知道为什么第一个设置会挂起或生成一个 0 字节文件作为输出,我也很想知道原因。

【问题讨论】:

  • bar 的标准输入不应该是foo 的标准输出而不是它的标准输入吗?
  • 应该:我打错了。我已经在我的问题中解决了它。
  • 如果您只是将您的标准输入复制到孩子的标准输入,那么firstProcess = Popen(['foo', '-'], stdin=sys.stdin, stdout=PIPE) 有效。

标签: python io subprocess io-redirection


【解决方案1】:

shell=True 之所以有效,是因为您要求外壳程序解释您的整个命令行并自行处理管道。就好像你直接在 shell 中输入了foo - | bar -

(这也是为什么使用 shell=True 可能不安全的原因;有很多方法可以欺骗 shell 做坏事,如果你直接将命令和参数作为列表传递,那么这些事情就不会发生' t 受制于任何中介机构的解析。)

【讨论】:

  • 我想我添加了一个错字,并打算按照您的方式进行操作。事实上,我检查过,情况就是这样。我会相应地编辑我的问题。
  • 明白了。我删除了代码,因为它现在是多余的。我应该补充一点,它对我有一些基本的 foobar 替代品(sortuniq,实际上),在 stdin 上阅读一些随机文本。 (进一步思考,这可能是因为uniq 不会在sort 之前退出......)
【解决方案2】:

要修复第一个示例,请将foo_process.stdout.close() 添加为the docs suggest。以下代码模拟foo - | bar - 命令:

#!/usr/bin/python
from subprocess import Popen, PIPE

foo_process = Popen(['foo', '-'], stdout=PIPE)
bar_process = Popen(['bar', '-'], stdin=foo_process.stdout)
foo_process.stdout.close() # allow foo to know if bar ends
bar_process.communicate()  # equivalent to bar_process.wait() in this case  

你不需要在这里明确使用sys.stdinsys.stdout,除非它们与sys.__stdin__sys.__stdout__不同。

模拟foo - &lt; data | bar - &gt; result 命令:

#!/usr/bin/python
from subprocess import Popen, PIPE

with open('data','rb') as input_file, open('result', 'wb') as output_file:
    foo = Popen(['foo', '-'], stdin=input_file, stdout=PIPE)
    bar = Popen(['bar', '-'], stdin=foo.stdout, stdout=output_file)
    foo.stdout.close() # allow foo to know if bar ends
bar.wait()

如果您想将修改后的输入逐行提供给foo 进程,即模拟python modify_input.py | foo - | bar - 命令:

#!/usr/bin/python
import sys
from subprocess import Popen, PIPE

foo_process = Popen(['foo', '-'], stdin=PIPE, stdout=PIPE)
bar_process = Popen(['bar', '-'], stdin=foo_process.stdout)
foo_process.stdout.close() # allow foo to know if bar ends
for line in sys.stdin:
    print >>foo_process.stdin, "PY", line, # modify input, feed it to `foo`
foo_process.stdin.close() # tell foo there is no more input
bar_process.wait()

【讨论】:

  • 我为什么要使用bar_process.communicate()?我不会写数据到foo_process吗?
  • communicate() 可能会代替 wait(),因为 bar_process 永远不会向 Python 提供输出。
  • @AlexReynolds:代码模拟您的第一个示例(无需先从标准输入读取,只需立即将其写入foofoo 进程可以自行从标准输入读取)。
  • @AlexReynolds: bar_process.stdin is None 所以在这种情况下bar_process.communicate() 就是bar_process.wait()
  • 不幸的是,这不起作用。无论如何,我需要一次处理一行数据,并且还能够处理stdin以外的文件句柄。它看起来不像 for line in sys.stdin: ... bar_process.communicate(line) 工作。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-05-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-03-25
  • 2019-09-28
  • 1970-01-01
相关资源
最近更新 更多