Eu tenho um arquivo de 1 TB. Gostaria de ler do byte 12345678901 ao byte 19876543212 e colocá-lo na saída padrão em uma máquina com 100 MB de RAM.
Eu posso escrever facilmente um script perl que faça isso. O sysread fornece 700 MB / s (o que é bom), mas o syswrite fornece apenas 30 MB / s. Gostaria de algo mais eficiente, de preferência algo instalado em todos os sistemas Unix e que possa entregar na ordem de 1 GB / s.
Minha primeira ideia é:
dd if=1tb skip=12345678901 bs=1 count=$((19876543212-12345678901))
Mas isso não é eficiente.
Editar:
Eu não tenho idéia de como eu medi a syswrite errado. Isso fornece 3,5 GB / s:
perl -e 'sysseek(STDIN,shift,0) || die; $left = shift; \
while($read = sysread(STDIN,$buf, ($left > 32768 ? 32768 : $left))){ \
$left -= $read; syswrite(STDOUT,$buf);
}' 12345678901 $((19876543212-12345678901)) < bigfile
e evita o yes | dd bs=1024k count=10 | wc
pesadelo.
bs=1M iflag=skip_bytes,count_bytes
Respostas:
Isso é lento devido ao tamanho pequeno do bloco. Usando um GNU recente
dd
( coreutils v8.16 + ), a maneira mais simples é usar as opçõesskip_bytes
ecount_bytes
:Atualizar
fullblock
opção adicionada acima, conforme resposta do @Gilles . A princípio, pensei que isso poderia estar implícitocount_bytes
, mas esse não é o caso.Os problemas mencionados são um possível problema abaixo, se
dd
as chamadas de leitura / gravação forem interrompidas por qualquer motivo, os dados serão perdidos. Isso não é provável na maioria dos casos (as probabilidades são um pouco reduzidas, pois estamos lendo de um arquivo e não de um pipe).Usar um
dd
sem as opçõesskip_bytes
ecount_bytes
é mais difícil:Você também pode experimentar diferentes tamanhos de bloco, mas os ganhos não serão muito drásticos. Consulte - Existe uma maneira de determinar o valor ideal para o parâmetro bs para dd?
fonte
bs
não for um fator deskip
?skip
há vários blocos, não bytes. Talvez você esteja confuso, poisskip_bytes
é usado no primeiro exemplo, o significadoskip
está em bytes lá?bs
é4,096
, o que significa que você não pode ignorar com mais precisão que4,096
bytesdd
com a primeira e a última utilizaçãobs=1
, a fim de copiar os dados que não iniciam ou terminam em um alinhamento de bloco.bs=1
dizdd
para ler e escrever um byte de cada vez. Existe uma sobrecarga para cada chamadaread
ewrite
, o que torna isso lento. Use um tamanho de bloco maior para obter um desempenho decente.Quando você copia um arquivo inteiro, pelo menos no Linux, descobri isso
cp
ecat
é mais rápido quedd
, mesmo se você especificar um tamanho de bloco grande.Para copiar apenas parte de um arquivo, você pode
tail
entrar nohead
. Isso requer coreutils GNU ou alguma outra implementação que precisehead -c
copiar um número especificado de bytes (tail -c
está no POSIX, mashead -c
não está). Uma referência rápida no Linux mostra que isso é mais lento do quedd
, presumivelmente por causa do pipe.O problema
dd
é que não é confiável: ele pode copiar dados parciais . Tanto quanto sei,dd
é seguro ao ler e gravar em um arquivo comum - consulte Quando o dd é adequado para copiar dados? (ou, quando são lidos () e gravados () parciais) - mas apenas desde que não sejam interrompidos por um sinal . Com o GNU coreutils, você pode usar afullblock
flag, mas isso não é portátil.Outro problema
dd
é que pode ser difícil encontrar uma contagem de blocos que funcione, porque o número de bytes ignorados e o número de bytes transferidos precisam ser múltiplos do tamanho do bloco. Você pode usar várias chamadas paradd
: uma para copiar o primeiro bloco parcial, uma para copiar a maior parte dos blocos alinhados e uma para copiar o último bloco parcial - consulte a resposta de Graeme para obter um snippet de shell. Mas não se esqueça de que quando você executa o script, a menos que esteja usando afullblock
bandeira, você precisa rezar para quedd
todos os dados sejam copiados.dd
retorna um status diferente de zero se uma cópia é parcial, por isso é fácil detectar o erro, mas não há maneira prática de repará-lo.O POSIX não tem nada melhor para oferecer no nível do shell. Meu conselho seria escrever um pequeno programa C para fins especiais (dependendo exatamente do que você implementa, você pode chamá-lo
dd_done_right
outail_head
oumini-busybox
).fonte
yes | dd bs=1024k count=10 | wc
problema antes. Desagradável.Com
dd
:Alternativamente com
losetup
:E então
dd
,cat
... o dispositivo de loop.fonte
É assim que você pode fazer isso:
Isso é tudo o que é realmente necessário - não requer muito mais. Em primeiro lugar,
dd count=0 skip=1 bs=$block_size1
irálseek()
sobre a entrada regular de arquivos praticamente instantaneamente. Não há chance de dados perdidos ou quaisquer outras mentiras contadas a respeito, basta procurar diretamente a posição inicial desejada. Como o descritor de arquivo pertence ao shell e odd
meramente o está herdando, eles afetarão sua posição do cursor e, portanto, você poderá segui-lo em etapas. É realmente muito simples - e não há ferramenta padrão mais adequada para a tarefa do quedd
.Isso usa um tamanho de bloco de 64k, o que geralmente é ideal. Ao contrário da crença popular, tamanhos maiores de blocos não tornam o
dd
trabalho mais rápido. Por outro lado, tampões minúsculos também não são bons.dd
precisa sincronizar seu tempo nas chamadas do sistema para que não precise esperar a cópia dos dados na memória e para fora novamente, mas também para que não precise esperar nas chamadas do sistema. Portanto, você quer que demore tempo suficiente para que o próximoread()
não precise esperar pelo último, mas não tanto que você esteja armazenando buffer em tamanhos maiores do que o necessário.Então o primeiro
dd
pula para a posição inicial. Isso leva tempo zero . Você poderia ligar para qualquer outro programa que você gostasse naquele momento para ler o stdin e ele começaria a ler diretamente no deslocamento de bytes desejado. Eu chamo outrodd
para ler((interval / blocksize) -1)
blocos de contagem para stdout.A última coisa necessária é copiar o módulo (se houver) da operação de divisão anterior. E é isso.
A propósito, não acredite quando as pessoas demonstram fatos sem comprovação. Sim, é possível
dd
fazer uma leitura curta (embora essas coisas não sejam possíveis ao ler a partir de um dispositivo de bloco íntegro - portanto, o nome) . Essas coisas só são possíveis se você não armazenar em buffer corretamente umdd
fluxo lido de outro dispositivo que não seja um bloco. Por exemplo:Nos dois casos,
dd
copia todos os dados. No primeiro caso, é possível (embora improvávelcat
) que alguns dos blocos de saída copiados tenhamdd
bits iguais a "$ num" bytes porquedd
são especificados apenas para armazenar em buffer qualquer coisa quando o buffer for solicitado especificamente em seu comando. linha.bs=
representa um tamanho máximo de bloco porque o objetivo dedd
é a E / S em tempo real.No segundo exemplo, especifico explicitamente o tamanho do bloco de saída e as
dd
leituras dos buffers até que as gravações completas possam ser feitas. Isso não afeta ocount=
que é baseado em blocos de entrada, mas para isso você só precisa de outrodd
. Qualquer informação incorreta que lhe seja fornecida deve ser desconsiderada.fonte