связать несколько команд Popen с каналами - PullRequest
25 голосов
/ 12 сентября 2011

Я знаю, как запустить команду, используя cmd = subprocess.Popen, а затем subprocess.communicate.Большую часть времени я использую строку с токеном shlex.split в качестве аргумента argv для Popen.Пример с "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]

Можете ли вы сказать мне, чтоЯ делаю неправильно, пожалуйста?

Thx

Ответы [ 4 ]

41 голосов
/ 12 сентября 2011

Я думаю, что вы хотите создать здесь два отдельных объекта Popen, один для 'ls', а другой для 'sed'.Вы захотите передать атрибут stdout первого объекта Popen в качестве аргумента stdin второму объекту Popen.

Пример:

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, ...)

См. документацию для подпроцесса для получения дополнительной информации о том, как работать с подпроцессами.

3 голосов
/ 20 апреля 2015

Я сделал небольшую функцию, чтобы помочь с трубопроводом, надеюсь, это поможет. Он будет цеплять Попенса по мере необходимости.

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
2 голосов
/ 12 сентября 2011

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]

НТН

1 голос
/ 22 марта 2017
"""
Why don't you use shell

"""

def output_shell(line):

    try:
        shell_command = Popen(line, stdout=PIPE, stderr=PIPE, shell=True)
    except OSError:
        return None
    except ValueError:
        return None

    (output, err) = shell_command.communicate()
    shell_command.wait()
    if shell_command.returncode != 0:
        print "Shell command failed to execute"
        return None
    return str(output)
...