Acabo de llegar al problema de tener que cortar algunas líneas de un archivo de gran tamaño (gigabytes) y, siendo consciente de que un posible consumidor de CPU intentaba leerlo en la memoria, quería editarlo en el lugar... y entonces Me encontré con estas preguntas:
- ¿Cómo elimino ciertas líneas (usando números de línea) en un archivo?
- ¿Hay alguna forma de modificar un archivo in situ?
... y además también estos:
Sin embargo, estaba dando vueltas sobre otra cosa: creo (pero no estoy seguro) que cualquier sistema de archivos (como ext3
) tendría que emplear algo así como una lista enlazada, para poder describir algo así como fragmentos de un archivo que están asignados a áreas del disco.
Por lo tanto, debería ser posible hacer algo como esto; por ejemplo, digamos que tengo un archivo bigfile.dat
como este (los números deben indicar el desplazamiento de bytes, pero es un poco difícil alinearlos):
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
L 1\n L 2\n L 3\n L 4\n L 5\n L 6\n
Este archivo podría, en principio, cargarse en una aplicación de terminal para navegar; imaginemos que llamamos a una herramienta editsegments bigfile.dat
y digámoslo de manera similar a como less -N bigfile.dat
se mostraría el mismo archivo (con números de línea):
1 1 L 1
2 2 L 2 *
3 3 L 3
4 4 L 4 *
5 5 L 5
6 6 L 6
bigfile.dat (END)
Digamos que podría ingresar un comando allí (por ejemplo, d
para eliminar líneas), hacer clic en otra tecla o con el mouse donde se indica arriba con *
, lo que significa que todo lo que se encuentra entre las líneas 2 y 4 debe eliminarse. El programa entonces respondería mostrando esto:
1 1 L 1
2 5 L 5
3 6 L 6
bigfile.dat (END)
Ahora podemos ver que la primera columna situada más a la izquierda muestra el número de línea "nuevo" (después del corte), la segunda columna es el número de línea "antiguo" (antes del corte), y luego sigue el contenido de la línea real.
Ahora, lo que imagino que sucede después de editsegments
salir de esta pseudoaplicación es que, ante todo, bigfile.dat
permanece intacta; sin embargo, ahora también habrá un archivo de texto adicional en el mismo directorio, por ejemplo bigfile.dat.segments
; con estos contenidos:
d 4:15 # line 2-4
bigfile.dat.iedit
... y además, aparecería un archivo especial (como un "enlace simbólico"), llamémoslo .
Ahora, básicamente, el resultado de todo esto sería que si intento abrir bigfile.dat.iedit
con algo como less -N bigfile.dat.iedit
, querría obtener el contenido "editado":
1 L 1
2 L 5
3 L 6
bigfile.dat (END)
... lo que podría lograrse, supongo, instruyendo de alguna manera al sistema operativo, que cuando $FILE.iedit
se abre, primero $FILE.segments
se debe abrir y leer; Esto d 4:15
indicaría que se deben omitir los bytes 4 a 15 del archivo original, lo que daría como resultado algo como:
0 1 2 3 4 5 6 7 8 9 10 11 12,3,4 15 16 17 18 19 20 21 22 23
L 1\n
L
2
\n
L
3
\n
L
4
\n
L 5\n
L 6\n
0 1 2 3 ------------------------------->16 17 18 19 20 21 22 23
En otras palabras -asumiendoque en un concepto de sistema de archivos de un archivo, cada byte de contenido también contiene un "enlace" al siguiente byte en la cadena; debería ser posible indicarle al sistema de archivos que establezca una nueva lista enlazada basada en un script y proporcione el contenido como lo representa esta lista enlazada modificada a través de un archivo especial (enlace simbólico o canalización).
Eso es lo que quise decir con "scripted" en el título: que la "nueva" lista enlazada puede controlarse mediante un archivo de script ( $FILE.segments
), editable por el usuario en un editor de texto (o generado por una aplicación de interfaz). Lo que quise decir con “multipass” es el hecho de que bigfile.dat
en este proceso no se modifica en absoluto; así que podría editar el primer gigabyte (original) hoy, guardando el progreso en ( $FILE.segments
) - luego podría editar el segundo gigabyte mañana, guardando nuevamente el progreso en ( $FILE.segments
), etc. - mientras tanto, el original bigfile.dat
no cambia.
Cuando se completen todas las ediciones, probablemente se podría llamar una especie de comando (por ejemplo, editsegments --finalize bigfile.dat
), que simplemente codificaría permanentemente la nueva lista vinculada como el contenido de bigfile.dat
(y en línea con eso, eliminaría bigfile.dat.segments
y bigfile.dat.iedit
). O incluso más fácil, uno podría simplemente hacer:
cp bigfile.dat.iedit /path/to/somewhere/else/bigfile.modified.dat
Por supuesto, además del d
comando eliminar script, r
también se podría tener un comando eplace, por ejemplo:
r 16:18 AAA
... diciendo: reemplace el contenido entre los bytes 16 y 18 con los siguientes 18-16+1=3 bytes después del espacio (es decir, el AAA
); de hecho, la lista vinculada podría "engancharse" al contenido del comando del script ( el cuadro a continuación que contiene también el d
elete):
0 1 2 3 4 5 6 7 8 9 10 11 12,3,4 15 16 17 18 19 20 21 22 23
L 1\n
L
2
\n
L
3
\n
L
4
\n
L
5
\n
L 6\n
0 1 2 3 ------------------------------->| | 19 20 21 22 23
.
.
.
.
.
.
.
\n
r
1
6
:
1
8
AAA
\n
.
.
.
.
Ahora, supongo que programas como hexedit
(como se mencionóaquí) cambian los archivos in situ, pero me gustaría tener el beneficio de la posibilidad de crear secuencias de comandos (aún mejor si pudiera regularse mediante una aplicación GUI, incluso si es una terminal), y el beneficio de no tener realmente el archivo original. modificado, hasta que se confirme que todas las ediciones son según lo requerido.
No estoy seguro de si algo como esto es posible en absoluto - e incluso si lo es, supongo que puede requerir un controlador dedicado (en lugar de sólo un programa de usuario)... Pero supongo que vale la pena preguntar de todos modos - ¿existe? ¿Algo como esto para Linux?
Muchas gracias de antemano por cualquier respuesta,
¡Saludos!
Respuesta1
La estructura de los archivos en el disco depende del sistema de archivos utilizado. Ninguno de los sistemas de archivos del mundo real utiliza listas vinculadas como usted describe (eso sería fseek(3)
insoportable). Lo más parecido a esto es el de Microsoft.GORDO, esencialmente moviendo los punteros fuera de los bloques de datos a una matriz que los sigue.
Pero la mayoría de los sistemas de archivos utilizan algunas referencias basadas en punteros a bloques de datos en el archivo, por lo que, en principio, se podría cortar un bloque de un archivo simplemente mezclando un puñado de punteros (no todo el contenido del archivo) y marcando un bloque en el archivo. medio del archivo como gratuito. Lamentablemente, esta no es una operación muy útil, los bloques de archivos son bastante grandes (normalmente 4 KB) y rara vez se alinean razonablemente con las estructuras del archivo (ya sean líneas u otras subdivisiones).
Respuesta2
Lo que usted describe suena mucho arepeticiónde un editor de textolista de rehacercontra el archivo original sin cambios al que selista de rehacerpertenece. Estoy bastante seguro de que gvim
tiene talpersistenteLista de deshacer/rehacer, que usted puede (?) utilizar, y sé que emacs
definitivamente tiene una lista de este tipo a la que probablemente podría convencer para que haga lo que quiera (a través de un elisp
script), por ejemplo.Guardar el historial de deshacer de Emacs entre sesiones.
Como nota al margen, desactivar todas las acciones no deseadas podría ser una buena idea para archivos tan grandes, por ejemplo:guardado automático,resaltado de sintaxis(lento en ungrandearchivo emacs), etc. y emacs en un sistema de 32 bits tiene 256 MBlímite de tamaño de archivo.
Ciertamente no será tan conciso como lo que sugirió, pero puede ser útil si no hay una gran cantidad de cambios.
Respuesta3
Generalmente, no se puede editar un archivo en el lugar sin guardarlo completo en la memoria. Supongo que lo que realmente quieres hacer es tener un archivo nuevo que sea una copia del anterior sin líneas específicas. Esto se puede lograr fácilmente usando las utilidades de Unix head
y tail
. Por ejemplo, para copiar todo excepto las líneas 5, 12 y 52 de un archivo, puede hacer
head -n 4 bigfile.dat > tempfile.dat
tail -n +6 bigfile.dat | head -n 6 >> tempfile.dat
tail -n +13 bigfile.dat | head -n 39 >> tempfile.dat
tail -n 53 bigfile.dat >> tempfile.dat
En caso de que no estés familiarizado con estas utilidades, te las explicaré con más detalle.
La head
utilidad imprime las primeras n líneas de un archivo. Si no se le proporciona un argumento posicional, utilizará la entrada estándar como archivo. La -n
bandera le indica al cabezal cuántas líneas imprimir. Entonces, head -n 2
imprimirá solo las primeras 2 líneas de la entrada estándar.
La tail
utilidad imprime las últimas n líneas de un archivo. Al igual que head, puede leer desde un archivo o una entrada estándar. La bandera -n le dice a tail cuántas líneas imprimir desde el final. También puede anteponer el número con un signo más para indicarle a tail que imprima las líneas desde el final del archivo comenzando con esa cantidad de líneas desde el principio. Por ejemplo, tail -n 2
imprime las dos últimas líneas de la entrada estándar. Sin embargo, tail -n +2
imprime todas las líneas que comienzan con la línea número 2 (omite la línea 1).
Entonces, en general, si desea imprimir líneas en el rango [x, y) de un archivo, haría
`tail -n +x | head -n d`
donde d = y - x. Estos comandos producirán un nuevo archivo. Luego puede eliminar el archivo antiguo si lo desea. La ventaja de hacerlo de esta manera es que head
solo tail
necesita mantener una línea en la memoria a la vez, por lo que no llenará rápidamente su RAM.
Respuesta4
Suena como un trabajo para un guión sed. IIRC fue diseñado para tales tareas. El procesamiento línea por línea, el procesamiento repetido del mismo grupo de comandos y las expresiones regulares se combinan en una sola herramienta. Si bien sé que funcionará, no puedo guiarlo más que dirigirlo a su excelentepágina de manual.