Como concatenar resultados de múltiplos comandos e canalizar em outro sem arquivo intermediário?

1

Suponha que eu tenha quatro arquivos de texto muito grandes, todos compactados com xz.

file1.log.xz
file2.log.xz
file3.log.xz
file4.log.xz

O que eu gostaria de fazer é concatenar o conteúdo descompactado desses quatro arquivos em um novo arquivo file.xz . A coisa é, eu gostaria de não ter que passar por arquivos intermediários.

Os arquivos são arquivos de log muito grandes, com tamanho de gigabytes. Compactados, têm menos de 100 MB, mas se eu expandir os quatro arquivos e concatenar novamente, precisarei de pelo menos 30 GB de armazenamento para armazenar os arquivos descompactados. Eu poderia, é claro, então cat todos os arquivos descomprimidos em xz para recomprimi-los:

cat file1.log file2.log file3.log file4.log | xz -ve9 - > newfile.log.xz

Eu sei como concatenar dois arquivos na linha de comando sem um intermediário, assumindo que um não foi compactado e um foi compactado:

xz -d -c file2.log.xz | cat file1.log - | xz -ve9 - > files1and2.log.xz

Mas isso só funcionará para um arquivo, e um deles já deve estar descompactado.

Não tenho certeza se consigo apenas cat os vários arquivos .xz juntos - vamos supor que eles podem ter sido compactados com parâmetros diferentes.

Em um nível mais alto, a pergunta em si poderia ser feita: você pode pegar a saída de múltiplos (mais de dois) comandos, concatenar essas saídas e enviá-las para outro processo sem arquivos intermediários? (Cenário hipotético: imagine que estou fazendo algum tipo de processamento em todos os quatro arquivos muito grandes usando um script que gera saída para stdout e quer colocar a saída em outro arquivo compactado.)

É possível fazer isso usando apenas comandos de shell?

    
por fdmillion 27.03.2018 / 15:42

3 respostas

4

A documentação de xz diz

It is possible to concatenate .xz files as is. xz will decompress such files as if they were a single .xz file.

Nos meus testes, isso funciona mesmo se os arquivos diferentes forem compactados com opções diferentes; então no seu caso

cat -- *.log.xz > newfile.log.xz

funcionará bem.

Para responder à sua pergunta mais geral, você pode canalizar a saída de um comando composto, por exemplo,

for file in -- *.log.xz; do xzcat -- "$file"; done | xz -ve9 > newfile.log.xz

ou qualquer subshell. Isso permitiria que você executasse qualquer processamento que desejasse em seus arquivos de log antes de recomprimi-los. No entanto, no caso básico, isso também não é necessário; você pode descompactar e recompactar todos os seus arquivos executando

xzcat -- *.log.xz | xz -ve9 > newfile.log.xz

Se você adicionar -f , isso funciona com arquivos descompactados, então

xzcat -f -- uncompressed.log *.log.xz | xz -ve9 > newfile.log.xz

permitiria combinar registros descompactados e compactados.

    
por 27.03.2018 / 15:58
1

tente

for x in *.log.xz
do
  xz -d -c "$x"
done | xz -ve9 - > newfile.log.xz

(isso pode ser sublinhado, é claro).

para adicionar um novo arquivo não-comprimido, use um sub-shell ( () )

( cat newfile.log 
for x in *.log.xz
do
  xz -d -c "$x"
done ) | xz -ve9 - > newfile.log.xz
    
por 27.03.2018 / 15:52
0

xzcat -f é a resposta para a primeira parte da sua pergunta. Mas você está certo: você não pode simplesmente cat *xz | xzcat se alguns de seus arquivos forem compactados com -F lzma .

On a higher level, the question itself could be asked: can you take the output of multiple (more than two) commands, concatenate those outputs, and pipe them into another process without intermediate files?

O problema aqui é: Se você não armazenar a saída intermediária em arquivos onde faça você a armazena?

Se você armazená-lo na RAM, você está limitado pela quantidade de RAM livre. Se você for acima disso, sua máquina está indo rapidamente para o swaphell.

O GNU Parallel armazena arquivos temporários, mas se você os coloca em um sistema de arquivos tmpfs , eles são basicamente armazenados na RAM:

mkdir mytmp    
sudo mount tmpfs mytmp -t tmpfs -o rw,size=3P
parallel --tmpdir mytmp seq {}00000000 {}99999999 ::: 1 2 | grep 0000000

Se, no entanto, for aceitável misturar a saída linha a linha, você precisará armazenar apenas uma única linha de cada um dos programas em execução na RAM.

Isto é o que o GNU Parallel (> versão 20170822) faz:

parallel --lb seq {}00000000 {}99999999 ::: 1 2 | grep 0000000

Uma terceira solução é compactar os arquivos temporários usando um compressor rápido (por exemplo, pzstd , pigz , lz4 , lzop ):

parallel --compress seq {}00000000 {}99999999 ::: 1 2 | grep 0000000

(o GNU Parallel autodetecta qual compressor rápido você instalou).

    
por 29.03.2018 / 11:05