bash-style pipes in python?

faulkner faulkner891 at gmail.com
Thu Jul 12 05:55:48 CEST 2007


On Jul 11, 8:56 pm, Dan Stromberg - Datallegro
<dstromb... at datallegro.com> wrote:
> I'm constantly flipping back and forth between bash and python.
>
> Sometimes, I'll start a program in one, and end up recoding in the
> other, or including a bunch of python inside my bash scripts, or snippets
> of bash in my python.
>
> But what if python had more of the power of bash-style pipes?  I might not
> need to flip back and forth so much.  I could code almost entirely in python.
>
> The kind of thing I do over and over in bash looks like:
>
>         #!/usr/bin/env bash
>
>         # exit on errors, like python. Exit on undefind variables, like python.
>         set -eu
>
>         # give the true/false value of the last false command in a pipeline
>         # not the true/false value of the lat command in the pipeline - like
>         # nothing I've seen
>         set -o pipefail
>
>         # save output in "output", but only echo it to the screen if the command fails
>         if ! output=$(foo | bar 2>&1)
>         then
>                 echo "$0: foo | bar failed" 1>&2
>                 echo "$output" 1>&2
>                 exit 1
>         fi
>
> Sometimes I use $PIPESTATUS too, but not that much.
>
> I'm aware that python has a variety of pipe handling support in its
> standard library.
>
> But is there a similarly-simple way already, in python, of hooking the stdout of
> process foo to the stdin of process bar, saving the stdout and errors from both
> in a variable, and still having convenient access to process exit values?
>
> Would it be possible to overload | (pipe) in python to have the same behavior as in
> bash?
>
> I could deal with slightly more cumbersome syntax, like:
>
>         (stdout, stderrs, exit_status) = proc('foo') | proc('bar')
>
> ...if the basic semantics were there.
>
> How about it?  Has someone already done this?

class P(subprocess.Popen):
    def __or__(self, otherp):
        otherp.stdin.write(self.stdout.read())
        otherp.stdin.close()
        return otherp
    def __init__(self, cmd, *a, **kw):
        for s in ['out', 'in', 'err']: kw.setdefault('std' + s, -1)
        subprocess.Popen.__init__(self, cmd.split(), *a, **kw)

print (P('cat /etc/fstab') | P('grep x')).stdout.read()

of course, you don't need to overload __init__ at all, and you can
return otherp.stdout.read() instead of otherp, and you can make
__gt__, __lt__ read and write files. unfortunately, you can't really
fudge &>, >>, |&, or any of the more useful pipes, but you can make
more extensive use of __or__:

class Pipe:
    def __or__(self, other):
        if isinstance(other, Pipe): return ...
        elif isinstance(other, P): return ...
    def __init__(self, pipe_type): ...

k = Pipe(foo)
m = Pipe(bar)

P() |k| P()
P() |m| P()




More information about the Python-list mailing list