¿La mejor manera de eliminar bytes desde el inicio de un archivo?


62

Hoy tuve que eliminar los primeros 1131 bytes de un archivo mixto de texto / binario de 800 MB, un volcado de subversión filtrado que estoy pirateando para un nuevo repositorio. ¿Cuál es la mejor manera de hacer esto?

Para empezar intenté

dd bs=1 skip=1131 if=filtered.dump of=trimmed.dump

pero después del salto, esto copia el resto del archivo un byte a la vez, es decir, muy lentamente. Al final trabajé, necesitaba 405 bytes para redondear esto a tres bloques de 512 que podía omitir.

dd if=/dev/zero of=405zeros bs=1 count=405
cat 405zeros filtered.dump | dd bs=512 skip=3 of=trimmed.dump

que se completó bastante rápido, pero debe haber habido una manera más simple / mejor? ¿Hay alguna otra herramienta que haya olvidado? ¡Gracias!


ddes la herramienta adecuada para el trabajo: parece que se le ocurrió una solución agradable y elegante para su problema.
Justin Ethier

Respuestas:


64

Puede cambiar bs y omitir opciones:

dd bs=1131 skip=1 if=filtered.dump of=trimmed.dump

De esta manera, la operación puede beneficiarse de un bloqueo mayor.

De lo contrario, podría intentar con tail (aunque no es seguro usarlo con archivos binarios):

tail -c +1132 filtered.dump >trimmed.dump

Finalmente, puede usar instancias de 3 dd para escribir algo como esto:

dd if=filtered.dump bs=512k | { dd bs=1131 count=1 of=/dev/null; dd bs=512k of=trimmed.dump; }

donde el primer dd imprime su salida estándar filter.dump; el segundo solo lee 1131 bytes y los tira; luego, el último lee de su entrada estándar los bytes restantes de filter.dump y los escribe en trimmed.dump.


66
¡Gracias! No sabía que la entrada canalizada se transfirió a un segundo proceso como ese, eso es muy bueno. Sin embargo, no puedo creer que no haya pensado bs=1131 skip=1: - /
Rup

2
La mayoría de las implementaciones modernas de utilidades de shell funcionan correctamente con archivos binarios (es decir, no tienen problemas con los caracteres nulos y no insertarán una nueva línea adicional al final del archivo). Ciertamente, las implementaciones de GNU y * BSD son seguras.
Gilles 'SO- deja de ser malvado'

¿Qué significa "no es seguro usarlo con archivos binarios"?
Scott

18

No estoy seguro de cuándo skip_bytesse agregó, pero para omitir los primeros 11 bytes que tiene:

# echo {123456789}-abcdefgh- | 
                              dd bs=4096 skip=11 iflag=skip_bytes
-abcdefgh-
0+1 records in
0+1 records out
11 bytes (11 B) copied, 6.963e-05 s, 158 kB/s

Where iflag=skip_bytesle dice a dd que interprete el valor de la skipopción como bytes en lugar de bloques, lo que lo hace sencillo.


Sin duda, una ventaja de velocidad para archivos grandes y una pequeña cantidad de datos para eliminar.
sstn

Esta es la mejor respuesta, ya que funciona para todos los tamaños de bloque, por ejemploiflag=skip_bytes skip=1234 bs=1M
phiresky

15

Puede usar un sub-shell y dos ddllamadas como esta:

$ ( dd bs=1131 count=1 of=dev_null && dd bs=4K of=out.mp3 ) < 100827_MR029_LobbyControl.mp3
1+0 records in
1+0 records out
1131 bytes (1.1 kB) copied, 7.9691e-05 s, 14.2 MB/s
22433+1 records in
22433+1 records out
91886130 bytes (92 MB) copied, 0.329823 s, 279 MB/s
$ ls -l *
-rw------- 1 max users 91887261 2011-02-03 22:59 100827_MR029_LobbyControl.mp3
-rw-r--r-- 1 max users     1131 2011-02-03 23:04 dev_null
-rw-r--r-- 1 max users 91886130 2011-02-03 23:04 out.mp3
$ cat dev_null out.mp3 > orig
$ cmp 100827_MR029_LobbyControl.mp3 orig

1
Gracias. No sabía que la entrada canalizada continuaba a un segundo proceso como ese, ¿supongo que ese es el sub shell? ¡Definitivamente lo recordaré! Le di a Marco la señal porque llegó aquí primero pero +1 y ¡gracias por la respuesta!
Rup

1
@Rup, sí, el subconjunto, creado a través de paréntesis, proporciona un descriptor de archivo stdin y ambas llamadas dd consumen sucesivamente la entrada del mismo. Sí, Marco me ganó por 29 segundos :)
maxschlepzig

6

Si el sistema de archivos y el kernel de Linux lo admiten, puede probar fallocatesi desea realizar los cambios en el lugar: en el mejor de los casos, no hay datos IO en absoluto:

$ fallocate <magic> -o 0 -l 1131 inplace.dump

donde <magic>depende del sistema de archivos, la versión de Linux y el tipo de archivo ( FALLOC_FL_COLLAPSE_RANGEo FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZEpodría usarse internamente ).


1
Este es mi método preferido, pero ejecutar esto en un contenedor tiene sus problemas. stackoverflow.com/questions/31155591/…
michaelcurry

3

Deberías usar count=0, eso es simple lseek()siempre que sea posible.

Me gusta esto:

{  dd bs=1131 skip=1 count=0; cat; } <filtered.dump >trimmed.dump

ddenviará lseek()el descriptor del archivo de entrada a un desplazamiento de 1131 bytes, y luego catsimplemente copiará lo que quede para la salida.


2

Otra forma de eliminar bytes iniciales de un archivo (sin usar dden absoluto) es usar xxdy / sedo tailrespectivamente.

bytes=$((1131*2))

xxd -p -c 256 filtered.dump | tr -d '\n' | sed "s/^.\{0,${bytes}\}//" | xxd -r -p > trimmed.dump

bytes=$((bytes + 1)) 
xxd -p -c 256 filtered.dump | tr -d '\n' | tail -c +${bytes} | xxd -r -p > trimmed.dump

Eso está bien, pero creo que prefiero simplemente trabajar con el archivo en binario en lugar de convertirlo hacia y desde hexadecimal.
Rup

2

@maxschlepzig solicita un revestimiento en línea. Aquí hay uno en perl. Se necesitan 2 argumentos: desde el byte y la longitud. El archivo de entrada debe estar dado por '<' y la salida estará en stdout:

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 < bigfile > outfile

Si la longitud es mayor que el archivo, se copiará el resto del archivo.

En mi sistema, esto entrega 3.5 GB / s.


Sin embargo, creo que su desafío de una línea fue lograr que usted probara que la solución del lenguaje de scripting era mejor que su solución de shell de una línea. Y prefiero el suyo: es más corto y más claro para mí. Si el tuyo funciona mejor es porque estás usando un tamaño de bloque más grande que él, que también se puede mejorar fácilmente en su versión.
Rup

@Rup Alas, pero no. Parece olvidar que eso ddno garantiza una lectura completa. Intenta: sí | dd bs = 1024k cuenta = 10 | wc unix.stackexchange.com/questions/17295/…
Ole Tange

Además, mi solución no leerá los bytes que no necesita (que podrían tener varios terabytes de longitud).
Ole Tange
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.