Выход подпроцесса Python3

Я хочу запустить утилиту подсчета слов Linux wc, чтобы определить количество строк в настоящее время в /var/log/syslog, поэтому я могу обнаружить, что она растет. Я пробовал различный тест, и пока я возвращаю результаты из wc, он включает как количество строк, так и команду (например, var/log/syslog).

Поэтому он возвращает: 1338/var/log/syslog Но мне нужно только подсчитать количество строк, поэтому я хочу отключить часть /var/log/syslog и просто сохранить 1338.

Я попытался преобразовать его в строку из байта, а затем лишив результат, но не радость. Такая же история для преобразования в строку и снятие, декодирование и т.д. - все не дают результат, который я ищу.

Вот некоторые примеры того, что я получаю, с 1338 строками в syslog:

  • b'1338/var/log/syslog\n '
  • 1338/var/log/syslog

Здесь некоторый тестовый код, который я написал, чтобы попытаться взломать этот орех, но нет решения:

import subprocess

#check_output returns byte string
stdoutdata = subprocess.check_output("wc --lines /var/log/syslog", shell=True)
print("2A stdoutdata: " + str(stdoutdata))
stdoutdata = stdoutdata.decode("utf-8")
print("2B stdoutdata: " + str(stdoutdata))    
stdoutdata=stdoutdata.strip()
print("2C stdoutdata: " + str(stdoutdata))    

Результатом этого является:

  • 2A stdoutdata: b'1338/var/log/syslog\n '

  • 2B stdoutdata: 1338/var/log/syslog

  • 2C stdoutdata: 1338/var/log/syslog

  • 2D stdoutdata: 1338/var/log/syslog

Ответ 1

Я предлагаю вам использовать subprocess.getoutput() как он делает именно то, что вы хотите - запустить команду в оболочке и получить ее строковый вывод (в отличие от вывода строки байта). Затем вы можете разделить на пробелы и захватить первый элемент из возвращаемого списка строк.

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

import subprocess
stdoutdata = subprocess.getoutput("wc --lines /var/log/syslog")
print("stdoutdata: " + stdoutdata.split()[0])

Ответ 2

Чтобы избежать вызова файлов оболочки и декодирования, которые могут быть произвольной последовательностью байтов (кроме '\0') на * nix, вы можете передать файл как stdin:

import subprocess

with open(b'/var/log/syslog', 'rb') as file:
    nlines = int(subprocess.check_output(['wc', '-l'], stdin=file))
print(nlines)

Или вы можете игнорировать любые ошибки декодирования:

import subprocess

stdoutdata = subprocess.check_output(['wc', '-l', '/var/log/syslog'])
nlines = int(stdoutdata.decode('ascii', 'ignore').partition(' ')[0])
print(nlines)

Ответ 3

Начиная с Python 3.6 вы можете заставить check_output() возвращать str вместо bytes, задав для него параметр кодирования:

check_output('wc --lines /var/log/syslog', encoding='UTF-8')

Но так как вы просто хотите считать, а split() и int() могут использоваться с bytes, вам не нужно беспокоиться о кодировке:

linecount = int(check_output('wc -l /var/log/syslog').split()[0])

Хотя с внешней программой некоторые вещи могут быть проще (например, подсчет записей строк журнала, напечатанных journalctl), в этом конкретном случае вам не нужно использовать внешнюю программу. Самое простое решение только для Python:

with open('/var/log/syslog', 'rt') as f:
    linecount = len(f.readlines())

Это имеет тот недостаток, что он читает весь файл в память; если это большой файл, вместо этого инициализируйте linecount = 0 прежде чем открывать файл и использовать for line in f: linecount += 1 вместо readlines() чтобы при подсчете в памяти for line in f: linecount += 1 только небольшая часть файла.

Ответ 4

Эквивалентный Курту Дж. Сэмпсону ответ также (он возвращает строку):

subprocess.check_output('wc -l /path/to/your/file | cut -d " " -f1', universal_newlines=True, shell=True)

из документов:

Если указаны кодировка или ошибки, или текст имеет значение true, файловые объекты для stdin, stdout и stderr открываются в текстовом режиме с использованием указанной кодировки и ошибок или значения по умолчанию io.TextIOWrapper. Аргумент universal_newlines эквивалентен тексту и предназначен для обратной совместимости. По умолчанию файловые объекты открываются в двоичном режиме.

Нечто похожее, но немного сложнее с использованием subprocess.run():

subprocess.run(command, shell=True, check=True, universal_newlines=True, stdout=subprocess.PIPE).stdout

так как subprocess.check_output() может быть эквивалентно subprocess.run().