Tengo un archivo de 1 TB. Me gustaría leer del byte 12345678901 al byte 19876543212 y ponerlo en la salida estándar en una máquina con 100 MB de RAM.
Puedo escribir fácilmente un script en perl que haga esto. sysread entrega 700 MB / s (lo cual está bien), pero syswrite solo entrega 30 MB / s. Me gustaría algo más eficiente, preferiblemente algo que esté instalado en todos los sistemas Unix y que pueda entregarse en el orden de 1 GB / s.
Mi primera idea es:
dd if=1tb skip=12345678901 bs=1 count=$((19876543212-12345678901))
Pero eso no es eficiente.
Editar:
No tengo idea de cómo midí syswrite mal. Esto ofrece 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
y evita la yes | dd bs=1024k count=10 | wcpesadilla.

bs=1M iflag=skip_bytes,count_bytesRespuestas:
Esto es lento debido al pequeño tamaño del bloque. Usando un GNU reciente
dd( coreutils v8.16 + ), la forma más simple es usar las opcionesskip_bytesycount_bytes:Actualizar
fullblockopción agregada arriba según la respuesta de @Gilles . Al principio pensé que podría estar implícitocount_bytes, pero este no es el caso.Los problemas mencionados son un problema potencial a continuación, si
ddlas llamadas de lectura / escritura se interrumpen por algún motivo, los datos se perderán. Esto no es probable en la mayoría de los casos (las probabilidades se reducen un poco ya que estamos leyendo desde un archivo y no desde una tubería).Usar un
ddsin las opcionesskip_bytesycount_byteses más difícil:También podría experimentar con diferentes tamaños de bloque, pero las ganancias no serán muy dramáticas. Ver - ¿Hay alguna manera de determinar el valor óptimo para el parámetro bs a dd?
fuente
bsno es un factor deskip?skiphay varios bloques, no bytes. ¿Quizás estás confundido ya queskip_bytesse usa en el primer ejemplo, el significadoskipestá en bytes allí?bses4,096, lo que significa que no puede omitir con mayor precisión esos4,096bytesddcon el primer y el último usobs=1para copiar los datos que no comienzan o terminan en una alineación de bloque.bs=1le diceddque lea y escriba un byte a la vez. Hay una sobrecarga para cada unoreadywritellamada, lo que hace que este lento. Use un tamaño de bloque más grande para un rendimiento decente.Cuando copia un archivo completo, al menos en Linux, lo he encontrado
cpycates más rápido quedd, incluso si especifica un tamaño de bloque grande.Para copiar sólo una parte de un archivo, se puede canalizar
tailahead. Esto requiere GNU coreutils o alguna otra implementación que tengahead -cque copiar un número específico de bytes (tail -cestá en POSIX perohead -cno lo está). Un punto de referencia rápido en Linux muestra que esto es más lento quedd, presumiblemente debido a la tubería.El problema
ddes que no es confiable: puede copiar datos parciales . Hasta donde yo sé,ddes seguro al leer y escribir en un archivo normal. Consulte ¿ Cuándo es adecuado dd para copiar datos? (o, cuando se lee () y escribe () parcial) , pero solo mientras no sea interrumpido por una señal . Con GNU coreutils, puede usar lafullblockbandera, pero esto no es portátil.Otro problema
ddes que puede ser difícil encontrar un recuento de bloques que funcione, porque tanto el número de bytes omitidos como el número de bytes transferidos deben ser un múltiplo del tamaño del bloque. Puede usar varias llamadas add: una para copiar el primer bloque parcial, una para copiar la mayor parte de los bloques alineados y otra para copiar el último bloque parcial; consulte la respuesta de Graeme para obtener un fragmento de shell. Pero no olvides que cuando ejecutas el script, a menos que estés usando lafullblockbandera, debes rezar para queddse copien todos los datos.dddevuelve un estado distinto de cero si una copia es parcial, por lo que es fácil detectar el error, pero no hay una forma práctica de repararlo.POSIX no tiene nada mejor que ofrecer a nivel de shell. Mi consejo sería escribir un programa en C para fines especiales pequeña (dependiendo exactamente lo que se implementa, se puede llamar así
dd_done_rightotail_headomini-busybox).fuente
yes | dd bs=1024k count=10 | wcproblema antes. Asqueroso.Con
dd:Alternativamente con
losetup:Y luego
dd,cat... el dispositivo de bucle.fuente
Así es como puedes hacer esto:
Eso es todo lo que es realmente necesario: no requiere mucho más. En primer lugar
dd count=0 skip=1 bs=$block_size1,lseek()sobre la entrada de archivos regulares prácticamente de forma instantánea. No hay posibilidad de que se pierdan datos o de cualquier otra falsedad que se les diga, solo puede buscar directamente a su posición de inicio deseada. Debido a que el descriptor de archivo es propiedad del shell y losdd's simplemente lo heredan, afectarán la posición del cursor y, por lo tanto, puede seguirlo paso a paso. Realmente es muy simple, y no existe una herramienta estándar más adecuada para la tarea quedd.Utiliza un tamaño de bloque de 64k que a menudo es ideal. Contrariamente a la creencia popular, los bloques de mayor tamaño no hacen que el
ddtrabajo sea más rápido. Por otro lado, los amortiguadores pequeños tampoco son buenos.ddnecesita sincronizar su hora en las llamadas al sistema para que no tenga que esperar para copiar datos en la memoria y volver a salir, sino también para que no tenga que esperar en las llamadas del sistema. Por lo tanto, desea que tome el tiempo suficiente para que el siguienteread()no tenga que esperar al último, pero no tanto como para almacenar en búfer en tamaños más grandes de lo necesario.Entonces el primero
ddsalta a la posición inicial. Eso lleva cero tiempo. Puede llamar a cualquier otro programa que le haya gustado en ese momento para leer su stdin y comenzaría a leer directamente en el desplazamiento de bytes deseado. Llamo a otroddpara leer los((interval / blocksize) -1)bloques de conteo para stdout.Lo último que es necesario es copiar el módulo (si lo hay) de la operación de división anterior. Y eso es eso.
No lo creas, por cierto, cuando las personas declaran hechos sin evidencia. Sí, es posible
ddhacer una lectura corta (aunque tales cosas no son posibles cuando se lee desde un dispositivo de bloque sano , de ahí el nombre) . Tales cosas solo son posibles si no almacena correctamente unaddsecuencia que se lee desde otro dispositivo que no sea un bloque. Por ejemplo:En ambos casos,
ddcopia todos los datos. En el primer caso, es posible (aunque improbablecat) que algunos de los bloques de salida que seddcopian serán iguales a "$ num" bytes porque soloddse especifica para almacenar algo cuando el buffer se solicita específicamente en su comando. línea. representa un tamaño de bloque máximo porque el propósito de es E / S en tiempo real.bs=ddEn el segundo ejemplo, especifico explícitamente el tamaño de bloque de salida y las
ddlecturas de almacenamiento intermedio hasta que se puedan realizar escrituras completas. Eso no afecta locount=que se basa en bloques de entrada, pero para eso solo necesita otrodd. Cualquier información errónea que se le proporcione de otra manera no se tendrá en cuenta.fuente