<p>不幸的是,它有一些边缘情况,shell会为您处理,或者shell会完全忽略您。一些问题:</p>
<ul>
<li><p>函数应该总是<code>wait()</code>让每个进程完成,否则您将得到所谓的<a href="https://stackoverflow.com/questions/3220676/what-are-zombies-and-what-causes-them-are-there-zombie-processes-and-zombie-obj">zombie processes</a>。</p></li>
<li><p>这些命令应该使用实管道相互连接,这样就不需要一次将整个输出读入内存。这是管道的正常工作方式。</p></li>
<li><p>在父进程中,每个管道的读取端都应该关闭,这样子进程就可以在下一个进程关闭其输入时正确地<code>SIGPIPE</code>。如果没有这一点,父进程可以保持管道打开,而子进程不知道退出,并且它可能永远运行。</p></li>
<li><p>子进程中的错误应作为异常引发,除了<code>SIGPIPE</code>。读者可以在最后一个进程上为<code>SIGPIPE</code>提出异常,这是一个练习,因为<code>SIGPIPE</code>不是这里所期望的,但是忽略它是无害的。</p></li>
</ul>
<p>请注意,<code>subprocess.DEVNULL</code>在python3.3之前不存在。我知道有些人仍然使用2.x,您必须手动打开<code>/dev/null</code>的文件,或者决定管道中的第一个进程与父进程共享<code>stdin</code>。你知道吗</p>
<p>代码如下:</p>
<pre><code>import signal
import subprocess
def run_pipe(*cmds):
"""Run a pipe that chains several commands together."""
pipe = subprocess.DEVNULL
procs = []
try:
for cmd in cmds:
proc = subprocess.Popen(cmd, stdin=pipe,
stdout=subprocess.PIPE)
procs.append(proc)
if pipe is not subprocess.DEVNULL:
pipe.close()
pipe = proc.stdout
stdout, _ = proc.communicate()
finally:
# Must call wait() on every process, otherwise you get
# zombies.
for proc in procs:
proc.wait()
# Fail if any command in the pipe failed, except due to SIGPIPE
# which is expected.
for proc in procs:
if (proc.returncode
and proc.returncode != -signal.SIGPIPE):
raise subprocess.CalledProcessError(
proc.returncode, proc.args)
return stdout
</code></pre>
<p>在这里我们可以看到它的作用。您可以看到管道正确地以<code>yes</code>(它一直运行到<code>SIGPIPE</code>)终止,并正确地以<code>false</code>失败(它总是失败)。你知道吗</p>
<pre><code>In [1]: run_pipe(["yes"], ["head", "-n", "1"])
Out[1]: b'y\n'
In [2]: run_pipe(["false"], ["true"])
-
CalledProcessError Traceback (most recent call last)
<ipython-input-2-db97c6876cd7> in <module>()
> 1 run_pipe(["false"], ["true"])
~/test.py in run_pipe(*cmds)
22 for proc in procs:
23 if proc.returncode and proc.returncode != -signal.SIGPIPE:
-> 24 raise subprocess.CalledProcessError(proc.returncode, proc.args)
25 return stdout
CalledProcessError: Command '['false']' returned non-zero exit status 1
</code></pre>