<p>与使用shell字符串并尝试用自己的方法解析它不同,我要求用户自己将命令作为单独的实体提供。这避免了明显的陷阱,即检测到一个<code>|</code>是命令的一部分,而不是用作shell管道。你要求他们提供命令作为一个字符串列表或一个单独的字符串,然后你将<code>shlex.split</code>取决于你想要公开的接口。在下面的例子中,我选择第一个是因为它的简单性。你知道吗</p>
<p>一旦有了单独的命令,一个简单的<code>for</code>循环就足以将前一个命令的输出通过管道传输到下一个命令的输入,如<a href="https://stackoverflow.com/questions/19318859/execute-shell-script-from-python-with-multiple-pipes">you have found yourself</a>:</p>
<pre><code>def pipe_subprocesses(*commands):
if not commands:
return
next_input = None
for command in commands:
p = subprocess.Popen(command, stdin=next_input, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
next_input = p.stdout
out, err = p.communicate()
if err:
print(err.decode().strip())
else:
print(out.decode())
</code></pre>
<p>用法是:</p>
<pre><code>>>> pipe_subprocesses(['ls', '-lhtr'], ['awk', '{print $9}'], ['wc', '-l'])
25
</code></pre>
<hr/>
<p>现在,这是一个快速和肮脏的方式得到它的设置,并似乎有工作,因为你想要它。但是这个代码至少有两个问题:</p>
<ol>
<li>泄漏zombies进程/打开的进程句柄,因为没有进程的退出代码,但收集了最后一个;操作系统保持资源打开,以便您这样做</li>
<li>您不能访问中途会失败的进程的信息。你知道吗</li>
</ol>
<p>为了避免这种情况,您需要维护一个已打开进程的列表,并为每个进程显式地<code>wait</code>。因为我不知道您的具体用例,所以我只返回第一个失败的进程(如果有)或最后一个失败的进程(如果没有),这样您就可以采取相应的行动:</p>
<pre><code>def pipe_subprocesses(*commands):
if not commands:
return
processes = []
next_input = None
for command in commands:
if isinstance(command, str):
command = shlex.split(command)
p = subprocess.Popen(command, stdin=next_input, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
next_input = p.stdout
processes.append(p)
for p in processes:
p.wait()
for p in processes:
if p.returncode != 0:
return p
return p # return the last process in case everything went well
</code></pre>
<p>我还引入了一些<code>shlex</code>作为示例,以便您可以混合原始字符串和已解析的列表:</p>
<pre><code>>>> pipe_subprocesses('ls -lhtr', ['awk', '{print $9}'], 'wc -l')
25
</code></pre>