【问题标题】:Sub-processing pipe write to file malfunction子处理管道写入文件故障
【发布时间】:2013-03-02 08:42:51
【问题描述】:

在 shell 中执行它会得到切实的结果:

wget -O c1 --no-cache "http://some.website" | sed "1,259d" c1 | sed "4,2002d"

在 Python 中执行此操作对我没有任何帮助:

subprocess.call(shlex.split("wget -O c1 --no-cache \"http://some.website/tofile\""))
c1 = open("c1",'w')
first = subprocess.Popen(shlex.split("sed \"1,259d\" c1"), stdout=subprocess.PIPE)

subprocess.Popen(shlex.split("sed \"4,2002d\""), stdin=first.stdout, stdout=c1)
c1.close()

这样做也没有结果:

c1.write(subprocess.Popen(shlex.split("sed \"4,2002d\""), stdin=first.stdout, stdout=subprocess.PIPE).communicate()[0])

“让我一无所获”是指文件中的空白输出。有没有人看到这里有什么不寻常的地方?

【问题讨论】:

  • 你在 Python 命令行解释器中试过这个吗?在此过程中的每个步骤中,各种变量显示了什么?
  • 它们是 subprocess.Popen 对象。 c1 当然只是一个文件对象。

标签: python shell unix subprocess pipe


【解决方案1】:

我总是使用plumbum 来运行外部命令。它提供了一个非常直观的界面,当然,它会帮我转义。

看起来像:

from plumbum.cmd import wget, sed
cmd1 = wget['-O', 'c1']['--no-cache']["http://some.website"]
cmd2 = sed["1,259d"]['c1'] | sed["4,2002d"]
print cmd1
cmd1()  # run it
print cmd2
cmd2()  # run it

【讨论】:

  • 这绝对是一个有用的工具,我得试试看。
【解决方案2】:

语句c1 = open("c1",'w') 打开文件c1 进行写入并截断任何现有数据,因此在调用sed 之前wget 写入文件的所有内容都会被删除。

无论如何,我认为shlex.split 通常很尴尬。我更喜欢手动构建 args 列表:

from subprocess import Popen, PIPE

p0 = Popen(['wget', '-O', '-', 'http://www.google.com'], stdout=PIPE)
p1 = Popen(['sed', '2,8d'], stdin=p0.stdout, stdout=PIPE) 
with open('c1', 'w') as c1:
    p2 = Popen(['sed', '2,7d'], stdin=p1.stdout, stdout=c1)
    p2.wait()

但是,没有明显的理由让 Python 程序员不得不调用 sed。 Python 有字符串方法和正则表达式。此外,您可以使用 urllib2.urlopen 代替 wget。

【讨论】:

  • 小修正,将 p0 放在 with 之外并在其后放置 p0.wait(),运行 p1 而不用管道连接到 p0,一切正常。是的,我意识到 urllib 可以完成工作,我只是想知道为什么这不起作用。不过感谢您的建议。
  • @eazar001:p1 正在从 p0 获取其数据。我测试过,它对我有用。但是,我确实忘记了wait
  • 我的解决方法是打开一个cc1 并输出到它。然后运行一个进程来简单地mv cc1 c1。有点脏,但它工作。嗯,关于等待。它发生了,我想 wget 需要一段时间才能完成它的工作。
【解决方案3】:

为什么不直接在管道中完成所有操作并将输出发送到文件?

wget -O - "http://www.google.com" | sed "1,259d" | sed "4,2002d" > c1

或者,如果您不想将其发送到文件,而是希望将其放在标准输出上:

wget -O - "http://www.google.com" | sed "1,259d" | sed "4,2002d"

如果你想用 Python 来做:

pipe = subprocess.Popen(shlex.split("wget -O - \"http://www.google.com\" | sed \"1,259d\" | sed \"4,2002d\""), stdout=subprocess.PIPE)
result = pipe.communicate()[0]

【讨论】:

  • 能够使用您的建议的变体,并且有效。感谢您的帮助。
【解决方案4】:

为了让那些或多或少遇到相同类型问题的人的生活更轻松,我决定发布最终修改后的代码,其中考虑了 cmets 关于c1 和数据覆盖.特别有趣的是communicate() 的使用,它有助于完全消除僵尸进程的任何表现,这非常令人恼火。另外,我发现在不需要管道的部分使用subprocess.call 很有用。最后不需要wait()。最后,远离sedwget 是个好主意,尤其是使用Python 的内置工具和urllib2

p0 = subprocess.call(shlex.split("wget -Oc1 --no-cache \"http://Some.website/tofile\""))
p1 = subprocess.Popen(shlex.split("sed \"1,261d\" c1"), stdout=subprocess.PIPE)

with open("cc1", 'w') as cc1:
    p2 = subprocess.Popen(shlex.split("sed \"3,2002d\""), stdin=p1.stdout, stdout=cc1)
    p2.communicate()
    p1.communicate()
    p3 = subprocess.call(shlex.split("mv cc1 c1"))

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-11-07
    • 1970-01-01
    • 2012-04-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多