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 | wc
pesadilla.
bs=1M iflag=skip_bytes,count_bytes
Respuestas:
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_bytes
ycount_bytes
:Actualizar
fullblock
opció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
dd
las 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
dd
sin las opcionesskip_bytes
ycount_bytes
es 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
bs
no es un factor deskip
?skip
hay varios bloques, no bytes. ¿Quizás estás confundido ya queskip_bytes
se usa en el primer ejemplo, el significadoskip
está en bytes allí?bs
es4,096
, lo que significa que no puede omitir con mayor precisión esos4,096
bytesdd
con el primer y el último usobs=1
para copiar los datos que no comienzan o terminan en una alineación de bloque.bs=1
le dicedd
que lea y escriba un byte a la vez. Hay una sobrecarga para cada unoread
ywrite
llamada, 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
cp
ycat
es 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
tail
ahead
. Esto requiere GNU coreutils o alguna otra implementación que tengahead -c
que copiar un número específico de bytes (tail -c
está en POSIX perohead -c
no 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
dd
es que no es confiable: puede copiar datos parciales . Hasta donde yo sé,dd
es 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 lafullblock
bandera, pero esto no es portátil.Otro problema
dd
es 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 lafullblock
bandera, debes rezar para quedd
se copien todos los datos.dd
devuelve 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_right
otail_head
omini-busybox
).fuente
yes | dd bs=1024k count=10 | wc
problema 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
dd
trabajo sea más rápido. Por otro lado, los amortiguadores pequeños tampoco son buenos.dd
necesita 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
dd
salta 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 otrodd
para 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
dd
hacer 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 unadd
secuencia que se lee desde otro dispositivo que no sea un bloque. Por ejemplo:En ambos casos,
dd
copia todos los datos. En el primer caso, es posible (aunque improbablecat
) que algunos de los bloques de salida que sedd
copian serán iguales a "$ num" bytes porque solodd
se 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=
dd
En el segundo ejemplo, especifico explícitamente el tamaño de bloque de salida y las
dd
lecturas 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