How do I use subprocess.Popen to connect multiple processes by pipes?

How do I execute the following shell command using the Python subprocess module?

echo "input data" | awk -f script.awk | sort > outfile.txt

The input data will come from a string, so I don't actually need echo . I've got this far, can anyone explain how I get it to pipe through sort too?

p_awk = subprocess.Popen(["awk","-f","script.awk"],
                          stdin=subprocess.PIPE,
                          stdout=file("outfile.txt", "w"))
p_awk.communicate( "input data" )

UPDATE : Note that while the accepted answer below doesn't actually answer the question as asked, I believe S.Lott is right and it's better to avoid having to solve that problem in the first place!


You'd be a little happier with the following.

import subprocess

awk_sort = subprocess.Popen( "awk -f script.awk | sort > outfile.txt",
    stdin=subprocess.PIPE, shell=True )
awk_sort.communicate( b"input datan" )

Delegate part of the work to the shell. Let it connect two processes with a pipeline.

You'd be a lot happier rewriting 'script.awk' into Python, eliminating awk and the pipeline.

Edit . Some of the reasons for suggesting that awk isn't helping.

[There are too many reasons to respond via comments.]

  • Awk is adding a step of no significant value. There's nothing unique about awk's processing that Python doesn't handle.

  • The pipelining from awk to sort, for large sets of data, may improve elapsed processing time. For short sets of data, it has no significant benefit. A quick measurement of awk >file ; sort file awk >file ; sort file and awk | sort awk | sort will reveal of concurrency helps. With sort, it rarely helps because sort is not a once-through filter.

  • The simplicity of "Python to sort" processing (instead of "Python to awk to sort") prevents the exact kind of questions being asked here.

  • Python -- while wordier than awk -- is also explicit where awk has certain implicit rules that are opaque to newbies, and confusing to non-specialists.

  • Awk (like the shell script itself) adds Yet Another Programming language. If all of this can be done in one language (Python), eliminating the shell and the awk programming eliminates two programming languages, allowing someone to focus on the value-producing parts of the task.

  • Bottom line: awk can't add significant value. In this case, awk is a net cost; it added enough complexity that it was necessary to ask this question. Removing awk will be a net gain.

    Sidebar Why building a pipeline ( a | b ) is so hard.

    When the shell is confronted with a | b a | b it has to do the following.

  • Fork a child process of the original shell. This will eventually become b.

  • Build an os pipe. (not a Python subprocess.PIPE) but call os.pipe() which returns two new file descriptors that are connected via common buffer. At this point the process has stdin, stdout, stderr from its parent, plus a file that will be "a's stdout" and "b's stdin".

  • Fork a child. The child replaces its stdout with the new a's stdout. Exec the a process.

  • The b child closes replaces its stdin with the new b's stdin. Exec the b process.

  • The b child waits for a to complete.

  • The parent is waiting for b to complete.

  • I think that the above can be used recursively to spawn a | b | c a | b | c a | b | c , but you have to implicitly parenthesize long pipelines, treating them as if they're a | (b | c) a | (b | c) .

    Since Python has os.pipe() , os.exec() and os.fork() , and you can replace sys.stdin and sys.stdout , there's a way to do the above in pure Python. Indeed, you may be able to work out some shortcuts using os.pipe() and subprocess.Popen .

    However, it's easier to delegate that operation to the shell.


    import subprocess
    
    some_string = b'input_data'
    
    sort_out = open('outfile.txt', 'wb', 0)
    sort_in = subprocess.Popen('sort', stdin=subprocess.PIPE, stdout=sort_out).stdin
    subprocess.Popen(['awk', '-f', 'script.awk'], stdout=sort_in, 
                     stdin=subprocess.PIPE).communicate(some_string)
    

    To emulate a shell pipeline:

    from subprocess import check_call
    
    check_call('echo "input data" | a | b > outfile.txt', shell=True)
    

    without invoking the shell (see 17.1.4.2. Replacing shell pipeline):

    #!/usr/bin/env python
    from subprocess import Popen, PIPE
    
    a = Popen(["a"], stdin=PIPE, stdout=PIPE)
    with a.stdin:
        with a.stdout, open("outfile.txt", "wb") as outfile:
            b = Popen(["b"], stdin=a.stdout, stdout=outfile)
        a.stdin.write(b"input data")
    statuses = [a.wait(), b.wait()] # both a.stdin/stdout are closed already
    

    plumbum provides some syntax sugar:

    #!/usr/bin/env python
    from plumbum.cmd import a, b # magic
    
    (a << "input data" | b > "outfile.txt")()
    

    The analog of:

    #!/bin/sh
    echo "input data" | awk -f script.awk | sort > outfile.txt
    

    is:

    #!/usr/bin/env python
    from plumbum.cmd import awk, sort
    
    (awk["-f", "script.awk"] << "input data" | sort > "outfile.txt")()
    
    链接地址: http://www.djcxy.com/p/13472.html

    上一篇: 如何管道stderr,而不是标准输出?

    下一篇: 如何使用subprocess.Popen通过管道连接多个进程?