Как повторить поведение тройника в Python при использовании подпроцесса? - PullRequest
59 голосов
/ 08 июня 2010

Я ищу решение Python, которое позволило бы мне сохранить вывод команды в файл, не скрывая его от консоли.

К вашему сведению: я спрашиваю о tee (как утилита командной строки Unix), а не о функции с тем же именем из модуля Python intertools.

Детали

  • Решение Python (без вызова tee, оно недоступно в Windows)
  • Мне не нужно вводить какие-либо данные в stdin для вызываемого процесса
  • У меня нет контроля над вызываемой программой. Все, что я знаю, это то, что он выведет что-то в stdout и stderr и вернет с кодом выхода.
  • Для работы при вызове внешних программ (подпроцесс)
  • Для работы как stderr, так и stdout
  • Возможность различать stdout и stderr, потому что я могу отображать только один из них на консоли или я могу попытаться вывести stderr, используя другой цвет - это означает, что stderr = subprocess.STDOUT не будет работать.
  • Вывод в реальном времени (прогрессивный) - процесс может продолжаться долго, и я не могу дождаться его завершения.
  • Python 3 совместимый код (важно)

Ссылки

Вот некоторые неполные решения, которые я нашел до сих пор:

Диаграмма http://blog.i18n.ro/wp-content/uploads/2010/06/Drawing_tee_py.png

Текущий код (вторая попытка)

#!/usr/bin/python
from __future__ import print_function

import sys, os, time, subprocess, io, threading
cmd = "python -E test_output.py"

from threading import Thread
class StreamThread ( Thread ):
    def __init__(self, buffer):
        Thread.__init__(self)
        self.buffer = buffer
    def run ( self ):
        while 1:
            line = self.buffer.readline()
            print(line,end="")
            sys.stdout.flush()
            if line == '':
                break

proc = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdoutThread = StreamThread(io.TextIOWrapper(proc.stdout))
stderrThread = StreamThread(io.TextIOWrapper(proc.stderr))
stdoutThread.start()
stderrThread.start()
proc.communicate()
stdoutThread.join()
stderrThread.join()

print("--done--")

#### test_output.py ####

#!/usr/bin/python
from __future__ import print_function
import sys, os, time

for i in range(0, 10):
    if i%2:
        print("stderr %s" % i, file=sys.stderr)
    else:
        print("stdout %s" % i, file=sys.stdout)
    time.sleep(0.1)
Реальный выход
stderr 1
stdout 0
stderr 3
stdout 2
stderr 5
stdout 4
stderr 7
stdout 6
stderr 9
stdout 8
--done--

Ожидаемый результат - упорядочить строки. Замечание: модификация Popen для использования только одной PIPE недопустима, потому что в реальной жизни я хочу делать разные вещи с помощью stderr и stdout.

Кроме того, даже во втором случае мне не удавалось получать данные в режиме реального времени, фактически все результаты были получены после завершения процесса. По умолчанию Popen не должен использовать буферы (bufsize = 0).

Ответы [ 9 ]

16 голосов
/ 27 июля 2012

Я вижу, что это довольно старый пост, но на тот случай, если кто-то все еще ищет способ сделать это:

proc = subprocess.Popen(["ping", "localhost"], 
                        stdout=subprocess.PIPE, 
                        stderr=subprocess.PIPE)

with open("logfile.txt", "w") as log_file:
  while proc.poll() is None:
     line = proc.stderr.readline()
     if line:
        print "err: " + line.strip()
        log_file.write(line)
     line = proc.stdout.readline()
     if line:
        print "out: " + line.strip()
        log_file.write(line)
13 голосов
/ 06 августа 2010

Наконец-то мне пришлось реализовать команду tee() на Python.

Вы можете получить его здесь http://github.com/pycontribs/tendo/blob/master/tendo/tee.py

В настоящее время он позволяет вам делать такие вещи, как:

 tee("python --v") # works just like os.system()

 tee("python --v", "log.txt") # file names

 tee("python --v", file_handle)

 import logging
 tee("python --v", logging.info) # receive a method

Единственным текущим ограничением является то, что он не может различать stderr и stdout, что означает, что он объединит их обоих.

7 голосов
/ 08 июня 2010

Это простой порт tee для Python.

import sys
sinks = sys.argv[1:]
sinks = [open(sink, "w") for sink in sinks]
sinks.append(sys.stderr)
while True:
  input = sys.stdin.read(1024)
  if input:
    for sink in sinks:
      sink.write(input)
  else:
    break

Я сейчас работаю на Linux, но это должно работать на большинстве платформ.


Теперь для части subprocess, я не знаю, как вы хотите «подключить» подпроцессы stdin, stdout и stderr к вашим stdin, stdout, stderr и приемники файлов, но я знаю, что вы можете сделать это:

import subprocess
callee = subprocess.Popen( ["python", "-i"],
                           stdin = subprocess.PIPE,
                           stdout = subprocess.PIPE,
                           stderr = subprocess.PIPE
                         )

Теперь вы можете получить доступ к callee.stdin, callee.stdout и callee.stderr, как к обычным файлам, что позволяет работать вышеупомянутому «решению». Если вы хотите получить callee.returncode, вам нужно сделать дополнительный звонок на callee.poll().

Будьте осторожны с записью в callee.stdin: если процесс завершился, когда вы это сделаете, может возникнуть ошибка (в Linux я получаю IOError: [Errno 32] Broken pipe).

3 голосов
/ 28 декабря 2010

В python есть тонкие проблемы / ошибки, связанные с подпроцессом. PIPE: http://bugs.python.org/issue1652

Очевидно, это было исправлено в python3 +, но не в python 2.7 и старше.Для этого вам нужно использовать: code.google.com/p/python-subprocess32/

3 голосов
/ 08 июня 2010

Если вы не хотите взаимодействовать с процессом, вы можете просто использовать модуль подпроцесса.

Пример:

tester.py

import os
import sys

for file in os.listdir('.'):
    print file

sys.stderr.write("Oh noes, a shrubbery!")
sys.stderr.flush()
sys.stderr.close()

testing.py

import subprocess

p = subprocess.Popen(['python', 'tester.py'], stdout=subprocess.PIPE,
                     stdin=subprocess.PIPE, stderr=subprocess.PIPE)

stdout, stderr = p.communicate()
print stdout, stderr

В вашей ситуации вы можете сначала записать в файл stdout / stderr.Вы также можете посылать аргументы в ваш процесс с помощью метода connect, хотя я не смог понять, как постоянно взаимодействовать с подпроцессом.

0 голосов
/ 06 июня 2019

Вот как это можно сделать

import sys
from subprocess import Popen, PIPE

with open('log.log', 'w') as log:
    proc = Popen(["ping", "google.com"], stdout=PIPE, encoding='utf-8')
    while proc.poll() is None:
        text = proc.stdout.readline() 
        log.write(text)
        sys.stdout.write(text)
0 голосов
/ 05 июня 2019

Мое решение не элегантное, но оно работает.

Вы можете использовать powershell для получения доступа к "тройнику" под WinOS.

import subprocess
import sys

cmd = ['powershell', 'ping', 'google.com', '|', 'tee', '-a', 'log.txt']

if 'darwin' in sys.platform:
    cmd.remove('powershell')

p = subprocess.Popen(cmd)
p.wait()
0 голосов
/ 23 сентября 2015

Я написал вещь, которая оборачивает команды оболочки в Python.

Ключевые преимущества:

  1. Эта утилита всегда захватывает stdout / stderr
  2. Эта утилита предоставляет возможность выводить stdout / stderr в stdout / stderr для процесса
  3. При выводе stdout / stderr out / err нет задержки

Ключевой недостаток:

  • Работает только на bash / unix

источник: https://gist.github.com/AndrewHoos/9f03c74988469b517a7a

0 голосов
/ 11 июня 2010

Попробуйте это:

import sys

class tee-function :

    def __init__(self, _var1, _var2) :

        self.var1 = _var1
        self.var2 = _var2

    def __del__(self) :

        if self.var1 != sys.stdout and self.var1 != sys.stderr :
            self.var1.close()
        if self.var2 != sys.stdout and self.var2 != sys.stderr :
            self.var2.close()

    def write(self, text) :

        self.var1.write(text)
        self.var2.write(text)

    def flush(self) :

        self.var1.flush()
        self.var2.flush()

stderrsav = sys.stderr

out = open(log, "w")

sys.stderr = tee-function(stderrsav, out)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...