2015-12-06 3 views
4

Я хочу перенаправить вывод subprocess.call(...) в файл с сжатым файлом xz- или bzip2.Запись и сжатие вывода subprocess.call

Я пробовал:

with lzma.open(log_path, "x") as log_file: 
    subprocess.call(command, stdout=log_file, stderr=log_file) 

но полученный файл не является допустимым XZ-сжатый файл:

$ xzcat logfile.xz 
xzcat : logfile.xz: Format de fichier inconnu 

(который, по-французски означает "неизвестный формат файла").

Когда я использую только cat, файл отображается правильно, с некоторыми фантастическими данными в конце (команда запущена в скрипте rsync):

& cat logfile.xz 
sending incremental file list 
prog/testfile 

sent 531.80K bytes received 2.71K bytes 1.07M bytes/sec 
total size is 14.21G speedup is 26,588.26 
�7zXZ�ִF�D!��}YZ 

logfile.xz кажется полу-действительный архивный файл XZ, заполненный несжатыми данными. Что я делаю не так ?

PS: Это работает, когда я делаю что-то вроде этого:

output = subprocess.check_output(command) 
log_file.write(output) 

... но, учитывая, что команда занимает много времени (это скрипт резервного копирования), я хочу, чтобы иметь возможность видеть журнал (с xzcat) до конца, чтобы узнать, что делает rsync.

ответ

2

Перенаправление происходит на уровне дескриптора файла до того, как ребенок еще выполнен: после этого не запускается родительский код (связанный с дочерним stdout/stderr) (код Python от lzma не запускается).

Для сжатия на лету, чтобы вы могли увидеть результат, пока дочерний процесс все еще работает, вы можете перенаправить вывод xz утилиты:

#!/usr/bin/env python3 
import subprocess 

with open('logfile.xz', 'xb', 0) as log_file: 
    subprocess.call("command | xz -kezc -", shell=True, 
        stdout=log_file, stderr=subprocess.STDOUT) 

Примечание: обычный open() используется, не lzma.open() : сжатие выполняется в подпроцессе xz.


Если вы хотите сжать в чистом коде Python, то вы должны трубы данные через python:

#!/usr/bin/env python3 
import lzma 
from subprocess import Popen, PIPE, STDOUT 
from shutil import copyfileobj 

with lzma.open('logfile.xz', 'xb') as log_file, \ 
    Popen('command', stdout=PIPE, stderr=STDOUT) as process: 
    copyfileobj(process.stdout, log_file) 

Примечание: lzma.open() используется.

+0

Спасибо! Этот «нет родительского кода» вещь странная. Я решил использовать чистый Python. – Arno

+0

@YdobEmos ничего странного, это то, как в оболочке работают конвейеры и перенаправление: 'command | another> output.txt 2> & 1' – jfs

+0

Мне кажется, что это неинтуитивно, я бы ожидал, что данные, которые я отправляю в файле LZMA, должны быть сжаты до того, как они будут записаны. (Я говорил о том, что ваш «родительский код» (связанный с дочерним stdout/stderr) запускается после этого (код Python из модуля lzma не запущен) «предложение». – Arno

Смежные вопросы