使用管道链接多个Popen命令

32 投票
5 回答
37549 浏览
提问于 2025-04-17 02:06

我知道怎么用cmd = subprocess.Popen来运行一个命令,然后用subprocess.communicate来获取结果。大多数时候,我会用shlex.split把一个字符串分割成一个个小部分,作为Popen的'argv'参数。比如说用"ls -l"这个命令:

import subprocess
import shlex
print subprocess.Popen(shlex.split(r'ls -l'), stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.PIPE).communicate()[0]

不过,管道似乎不太好使……比如下面这个例子就什么都没返回:

import subprocess
import shlex
print subprocess.Popen(shlex.split(r'ls -l | sed "s/a/b/g"'), stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.PIPE).communicate()[0]

你能告诉我我哪里做错了吗?

谢谢

5 个回答

4

shlex 这个工具只会根据命令行的规则来分割空格,但它不处理管道符号。

不过,它应该是这样工作的:

import subprocess
import shlex

sp_ls = subprocess.Popen(shlex.split(r'ls -l'), stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.PIPE)
sp_sed = subprocess.Popen(shlex.split(r'sed "s/a/b/g"'), stdin = sp_ls.stdout, stdout = subprocess.PIPE, stderr = subprocess.PIPE)
sp_ls.stdin.close() # makes it similiar to /dev/null
output = sp_ls.communicate()[0] # which makes you ignore any errors.
print output

根据 help(subprocess) 的说明

Replacing shell pipe line
-------------------------
output=`dmesg | grep hda`
==>
p1 = Popen(["dmesg"], stdout=PIPE)
p2 = Popen(["grep", "hda"], stdin=p1.stdout, stdout=PIPE)
output = p2.communicate()[0]

希望这对你有帮助

6

我写了一个小函数来帮助处理管道,希望对你有用。这个函数会根据需要串联多个Popen。

from subprocess import Popen, PIPE
import shlex

def run(cmd):
  """Runs the given command locally and returns the output, err and exit_code."""
  if "|" in cmd:    
    cmd_parts = cmd.split('|')
  else:
    cmd_parts = []
    cmd_parts.append(cmd)
  i = 0
  p = {}
  for cmd_part in cmd_parts:
    cmd_part = cmd_part.strip()
    if i == 0:
      p[i]=Popen(shlex.split(cmd_part),stdin=None, stdout=PIPE, stderr=PIPE)
    else:
      p[i]=Popen(shlex.split(cmd_part),stdin=p[i-1].stdout, stdout=PIPE, stderr=PIPE)
    i = i +1
  (output, err) = p[i-1].communicate()
  exit_code = p[0].wait()

  return str(output), str(err), exit_code

output, err, exit_code = run("ls -lha /var/log | grep syslog | grep gz")

if exit_code != 0:
  print "Output:"
  print output
  print "Error:"
  print err
  # Handle error here
else:
  # Be happy :D
  print output
49

我觉得你想在这里创建两个不同的Popen对象,一个用来执行'ls'命令,另一个用来执行'sed'命令。你需要把第一个Popen对象的stdout属性作为第二个Popen对象的stdin参数传递。

举个例子:

p1 = subprocess.Popen('ls ...', stdout=subprocess.PIPE)
p2 = subprocess.Popen('sed ...', stdin=p1.stdout, stdout=subprocess.PIPE)
print p2.communicate()

如果你有更多的命令,可以继续这样连接下去:

p3 = subprocess.Popen('prog', stdin=p2.stdout, ...)

想了解更多关于如何使用子进程的信息,可以查看subprocess文档

撰写回答