Eu não acho que você pode fazer isso - não de forma confiável, e não do jeito que você pergunta. O problema é que a taxa de compressão do arquivo provavelmente não será distribuída uniformemente da cabeça à cauda - o algoritmo de compressão se aplicará melhor a algumas partes do que a outras. É assim que funciona. E assim você não pode fatorar sua divisão no tamanho do arquivo compactado.
Além disso, gzip
não suporta o armazenamento do tamanho original de arquivos compactados com tamanho superior a 4 gbs - ele não pode lidar com isso. E assim você não pode consultar o arquivo para obter um tamanho confiável - porque ele vai enganar você.
A coisa das 4 linhas - é bem fácil, na verdade. A coisa de 4 arquivos - eu simplesmente não sei como você pode fazê-lo de forma confiável e com uma distribuição uniforme sem primeiro extrair o arquivo para obter o tamanho descompactado. Eu não acho que você pode porque eu tentei.
No entanto, o que você pode fazer é definir um tamanho máximo para os arquivos de saída divididos e garantir que eles sejam sempre quebrados nas barreiras de registro. Isso você pode fazer facilmente. Aqui está um pequeno script que fará isso extraindo o gzip
archive, e canalizando o conteúdo através de alguns dd
pipe-buffers explícitos com argumentos count=$rpt
específicos, antes de passar por lz4
para descompactar / recompactar cada arquivo no vôo. Eu também joguei alguns truques de pipe tee
para imprimir as últimas quatro linhas para cada segmento para stderr também.
( IFS= n= c=$(((m=(k=1024)*k)/354))
b=bs=354xk bs=bs=64k
pigz -d </tmp/gz | dd i$bs o$b |
while read -r line _$((n+=1))
do printf \n/tmp/lz4.$n\n
{ { printf %s\n "$line"
dd count=$c i$b o$bs
}| tee /dev/fd/3|lz4 -BD -9 >/tmp/lz4.$n
} 3>&1| tail -n4 |tee /dev/fd/2 |
wc -c;ls -lh /tmp/[gl]z*
done
)
Isso só vai continuar até que tenha manipulado todas as entradas. Ele não tenta dividi-lo por alguma porcentagem - que não pode ser obtida -, mas ao invés disso, ele divide por uma contagem máxima de bytes brutos por divisão. E de qualquer forma, uma grande parte do seu problema é que você não pode obter um tamanho confiável no seu arquivo porque é muito grande - faça o que fizer, não faça isso de novo - faça as partições menores que 4gbs por peça , talvez. Este pequeno script, pelo menos, permite que você faça isso sem nunca escrever um byte descompactado em disco.
Aqui está uma versão mais curta, despojada para o essencial - não adiciona todas as informações do relatório:
( IFS= n= c=$((1024*1024/354))
pigz -d | dd ibs=64k obs=354xk |
while read -r line _$((n+=1))
do { printf %s\n "$line"
dd count=$c obs=64k ibs=354xk
} | lz4 -BD -9 >/tmp/lz4.$n
done
) </tmp/gz
Ele faz todas as mesmas coisas que o primeiro, principalmente, ele simplesmente não tem muito a dizer sobre isso. Além disso, há menos confusão, então é mais fácil ver o que está acontecendo, talvez.
A coisa IFS=
é apenas para manipular a linha read
por iteração. Nós read
one porque precisamos que nosso loop termine quando a entrada terminar. Isso depende do seu registro tamanho - que, por exemplo, é de 354 bytes por. Eu criei um arquivo de 4 + gb gzip
com alguns dados aleatórios para testá-lo.
Os dados aleatórios foram assim:
( mkfifo /tmp/q; q="$(echo '[1+dPd126!<c]sc33lcx'|dc)"
(tr '/tmp/lz4.1
2961+1 records in
16383+1 records out
1073713090 bytes (1.1 GB) copied, 169.838 s, 6.3 MB/s
@NTACGTANTTCATTGGNATGACGCGCGTTTATGNGAGGGCGTCCGGAANGC+TCTCTNCC
TACGTANTTCATTGGNATGACGCGCGTTTATGNGAGGGCGTCCGGAANGCTCTCTNCCGAGCTCAGTATGTTNNAAGTCCTGANGNGTNGCGCCTACCCGACCACAACCTCTACTCGGTTCCGCATGCATGCAACACATCGTCA
+
I'AgZgW*,'Gw=KKOU:W5dE1m=-"9W@[AG8;<P7P6,qxE!7P4##,Q@c7<nLmK_u+IL4Kz.Rl*+w^A5xHK?m_JBBhqaLK_,o;p,;QeEjb|">Spg'MO6M'wod?z9m.yLgj4kvR~+0:.X#(Bf
354
-rw-r--r-- 1 mikeserv mikeserv 4.7G Jun 16 08:58 /tmp/gz
-rw-r--r-- 1 mikeserv mikeserv 652M Jun 16 12:32 /tmp/lz4.1
/tmp/lz4.2
2961+1 records in
16383+1 records out
1073713090 bytes (1.1 GB) copied, 169.38 s, 6.3 MB/s
@NTTGTTGCCCTAACCANTCCTTGGGAACGCAATGGTGTGANCTGCCGGGAC+CTTTTGCT
TTGTTGCCCTAACCANTCCTTGGGAACGCAATGGTGTGANCTGCCGGGACCTTTTGCTGCCCTGGTACTTTTGTCTGACTGGGGGTGCCACTTGCAGNAGTAAAAGCNAGCTGGTTCAACNAATAAGGACNANTTNCACTGAAC
+
>G-{N~Q5Z5QwV??I^~?rT+S0$7Pw2y9MV^BBTBK%HK87(fz)HU/0^%JGk<<1--7+r3e%X6{c#w@aA6Q^DrdVI0^8+m92vc>RKgnUnMDcU:j!x6u^g<Go?p(HKG@$4"T8BWZ<z.Xi
354
-rw-r--r-- 1 mikeserv mikeserv 4.7G Jun 16 08:58 /tmp/gz
-rw-r--r-- 1 mikeserv mikeserv 652M Jun 16 12:32 /tmp/lz4.1
-rw-r--r-- 1 mikeserv mikeserv 652M Jun 16 12:35 /tmp/lz4.2
-7-7' "$q$q"|fold -b144 >/tmp/q)&
tr '( IFS= n= c=$(((m=(k=1024)*k)/354))
b=bs=354xk bs=bs=64k
pigz -d </tmp/gz | dd i$bs o$b |
while read -r line _$((n+=1))
do printf \n/tmp/lz4.$n\n
{ { printf %s\n "$line"
dd count=$c i$b o$bs
}| tee /dev/fd/3|lz4 -BD -9 >/tmp/lz4.$n
} 3>&1| tail -n4 |tee /dev/fd/2 |
wc -c;ls -lh /tmp/[gl]z*
done
)
-7' '[A*60][C*60][G*60][N*16][T*]' | fold -b144 |
sed 'h;s/^\(.\{50\}\)\(.\{8\}\)/@N+\n/;P;s/.*/+/;H;x'|
paste "-d\n" - - - /tmp/q| dd bs=4k count=kx2k | gzip
) </dev/urandom >/tmp/gz 2>/dev/null
... mas talvez você não precise se preocupar tanto com isso, já que você já tem os dados e tudo. De volta à solução ...
Basicamente, pigz
- que parece descompactar um pouco mais rápido que o zcat
- canaliza o fluxo descompactado e dd
armazena essa saída em blocos de gravação dimensionados especificamente em um múltiplo de 354 bytes. O loop irá read
a $line
uma vez a cada iteração para testar se a entrada ainda está chegando, o que será printf
printf
at lz4
antes que outro dd
seja chamado para ler blocos dimensionados especificamente em um múltiplo de 354 bytes - para sincronizar com o buffer dd
process - pela duração. Haverá uma pequena leitura por iteração por causa do read $line
inicial - mas isso não importa, porque estamos imprimindo isso em lz4
- nosso processo de coletor - de qualquer forma.
Configurei-o para que cada iteração leia aproximadamente 1 GB de dados não compactados e comprima esse fluxo in-stream para cerca de 650 MB ou mais. lz4
é muito mais rápido do que qualquer outro método útil de compactação - que é a razão pela qual eu o escolhi aqui porque não gosto de esperar. O xz
faria um trabalho muito melhor na compactação real, provavelmente. Uma coisa sobre o lz4
, no entanto, é que ele pode ser descompactado perto da velocidade da RAM - o que significa que muitas vezes você pode descompactar um arquivo lz4
tão rápido quanto você poderia gravar na memória.
O grande faz alguns relatórios por iteração. Ambos os loops imprimirão o relatório de dd
sobre o número de bytes brutos transferidos e a velocidade e assim por diante. O loop grande também imprime as últimas 4 linhas de entrada por ciclo e uma contagem de bytes para o mesmo, seguido por um ls
do diretório no qual eu escrevo os lz4
archives. Aqui estão algumas rodadas de saída:
( IFS= n= c=$((1024*1024/354))
pigz -d | dd ibs=64k obs=354xk |
while read -r line _$((n+=1))
do { printf %s\n "$line"
dd count=$c obs=64k ibs=354xk
} | lz4 -BD -9 >/tmp/lz4.$n
done
) </tmp/gz