Выход подпроцесса 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().