Eficiente eliminación de encabezado en el lugar para archivos grandes usando sed?

24

Los siguientes comandos pueden tardar minutos dependiendo del tamaño del archivo. ¿Hay algún método más eficiente?

sed -i 1d large_file 
Cheng
fuente

Respuestas:

34

Intenta en su edlugar:

ed <<< $'1d\nwq' large_file

Si ese "grande" significa unos 10 millones de líneas o más, mejor uso tail. No es capaz de editar en el lugar, pero su rendimiento hace que esa falta sea perdonable:

tail -n +2 large_file > large_file.new

Editar para mostrar algunas diferencias de tiempo:

( awkcódigo de Jaypal agregado para tener tiempos de ejecución en la misma máquina (CPU 2.2GHz)).

bash-4.2$ seq 1000000 > bigfile.txt # further file creations skipped

bash-4.2$ time sed -i 1d bigfile.txt
time 0m4.318s

bash-4.2$ time ed -s <<< $'1d\nwq' bigfile.txt
time 0m0.533s

bash-4.2$ time perl -pi -e 'undef$_ if$.==1' bigfile.txt
time 0m0.626s

bash-4.2$ time { tail -n +2 bigfile.txt > bigfile.new && mv -f bigfile.new bigfile.txt; }
time 0m0.034s

bash-4.2$ time { awk 'NR>1 {print}' bigfile.txt > newfile.txt && mv -f newfile.txt bigfile.txt; }
time 0m0.328s
hombre trabajando
fuente
En el caso de tail, preferiría contar el tiempo para hacer tanto eliminar la primera línea y reemplazar bigfile.txtcon el bigfile.new.
rozcietrzewiacz
@rozcietrzewiacz, su punto es correcto. Gracias. Actualizado.
manatwork
¡Esto es realmente genial! Hice lo mismo con awky obtuve el siguiente resultado:[jaypal:~/Temp] seq 1000000 > bigfile.txt [jaypal:~/Temp] time awk 'NR>1 {print}' bigfile.txt >newfile.txt real 0m0.649s user 0m0.601s sys 0m0.033s
jaypal singh
1
@ Jaypal, agregué tu código a la lista de alternativas. En mi máquina fue aún más rápido. Extraño, esperaba que awkel rendimiento fuera más cercano al sedde. (Nota para mí: nunca esperes, prueba en su lugar)
Manatwork
Esta fue la mejor solución en mi caso: tail -n +2 bigfile.txt > bigfile.new && mv -f bigfile.new bigfile.txt;estoy usando un solo archivo con un bloqueo para realizar un seguimiento de una lista de tareas única utilizada por múltiples procesos. Empecé con lo que el cartel inicial utilizado: sed -i 1d large_file . Eso estaba causando que el archivo se bloqueara durante 1-2 segundos. El tail/mvcombo se completa casi instantáneamente. ¡Gracias!
Chris Adams
6

No hay forma de eliminar eficientemente las cosas desde el inicio de un archivo. Eliminar datos desde el principio requiere reescribir todo el archivo.

Sin embargo, el truncamiento desde el final de un archivo puede ser muy rápido (el sistema operativo solo tiene que ajustar la información del tamaño del archivo, posiblemente limpiando los bloques ahora no utilizados). Esto generalmente no es posible cuando intenta eliminar del encabezado de un archivo.

Teóricamente podría ser "rápido" si eliminara un bloque / extensión completo exactamente, pero no hay llamadas del sistema para eso, por lo que tendría que confiar en la semántica específica del sistema de archivos (si existe). (Supongo que tener alguna forma de desplazamiento dentro del primer bloque / extensión para marcar el inicio real del archivo. Nunca he oído hablar de eso tampoco).

Estera
fuente
Si el archivo es muy grande, es probable que la sobrecarga de E / S sea (posiblemente mucho) mayor que la sobrecarga de la CPU requerida para procesar el final de las líneas.
Mat
Tienes razón. Sin embargo, podría haber una diferencia en la forma en que las herramientas acceden al contenido del archivo. Lo mejor es no procesar línea por línea cuando no es necesario o al menos no leer línea por línea cuando no es necesario.
manatwork
2
Me sorprende que la diferencia sea tan grande en sus resultados, y puede reproducirla con ese tamaño de archivo aquí. Sin embargo, los beneficios parecen disminuir a medida que aumenta el tamaño del archivo (probado con seq 10M, 15s para sed, 5s para ed). Buenos consejos de todos modos (+1).
Mat
A partir de la versión 3.15, Linux ahora tiene una API para colapsar partes de un archivo en sistemas de archivos basados ​​en cierta extensión, pero al menos para ext4 que solo se puede hacer en bloques completos (generalmente 4k).
Stéphane Chazelas
Incluso si la edición requiere volver a escribir todo el archivo, a veces es muy útil tener herramientas de línea de comandos para editar de manera eficiente. En mi caso, esto ayudó cuando tuve que eliminar la primera línea de un archivo que era más grande que la RAM total del sistema.
Jason
3

El método más eficiente, ¡no lo hagas! Si lo hace, en cualquier caso, necesita el doble del espacio 'grande' en el disco y desperdicia las E / S.

Si está atascado con un archivo grande que desea leer sin la primera línea, espere hasta que necesite leerlo para eliminar la primera línea. Si necesita enviar el archivo desde stdin a un programa, use tail para hacerlo:

tail -n +2 | your_program

Cuando necesite leer el archivo, puede aprovechar la oportunidad para eliminar la primera línea, pero solo si tiene el espacio necesario en el disco:

tail -n +2 | tee large_file2 | your_program

Si no puede leer desde stdin, use un fifo:

mkfifo large_file_wo_1st_line
tail -n +2 large_file > large_file_wo_1st_line&
your_program -i large_file_wo_1st_line

incluso mejor si está usando bash, aproveche la sustitución del proceso:

your_program -i <(tail -n +2 large_file)

Si necesita buscar en el archivo, no veo una mejor solución que no quedar atascado con el archivo en primer lugar. Si este archivo fue generado por stdout:

large_file_generator | tail -n +2 > large_file

De lo contrario, siempre existe la solución de sustitución de procesos o fifo:

mkfifo large_file_with_1st_file
large_file_generator -o large_file_with_1st_file&
tail -n +2 large_file_with_1st_file > large_file_wo_1st_file

large_file_generator -o >(tail -n 2+ > large_file_wo_1st_file)
jfg956
fuente
1

Puede usar Vim en modo Ex:

ex -sc '1d|x' large_file
  1. 1 seleccione primera línea

  2. d borrar

  3. x guardar y cerrar

Steven Penny
fuente
0

Esto es solo teorizar, pero ...

Un sistema de archivos personalizado (implementado usando FUSE o un mecanismo similar) podría exponer un directorio cuyo contenido es exactamente el mismo que un directorio ya existente en otro lugar, pero con archivos truncados como desee. El sistema de archivos traduciría todas las compensaciones de archivos. Entonces no tendría que hacer una reescritura de un archivo que lleva mucho tiempo.

Pero dado que esta idea no es muy trivial, a menos que tenga decenas de terabytes de dichos archivos, implementar dicho sistema de archivos sería demasiado costoso / lento para ser práctico.

liori
fuente