A menos que sus segmentos sean realmente enormes (como en: realmente no puede ahorrar tanta RAM, presumiblemente porque este es un pequeño sistema incrustado que controla un sistema de archivos grande), una sola pasada es realmente el mejor enfoque. No solo porque será más rápido, sino más importante porque permite que la fuente sea una secuencia, desde la cual se pierden los datos leídos y no guardados. Esto es realmente un trabajo para awk, aunque sed también puede hacerlo.
sed -n -e 's/^---$//' -e 't a' \
-e 'H' -e '$g' -e '$s/^\n//' -e '$p' -e 'b' \
-e ':a' -e 'h' # you are not expected to understand this
awk '{if (/^---$/) {chunk=""} # separator ==> start new chunk
else {chunk=chunk $0 RS}} # append line to chunk
END {printf "%s", chunk}' # print last chunk (without adding a newline)
Si debe utilizar un enfoque de dos pasos, determine el desplazamiento de línea del último separador e imprima desde ese. O determine el desplazamiento de bytes e imprima a partir de eso.
</input/file tail -n +$((1 + $(</input/file # print the last N lines, where N=…
grep -n -e '---' | # list separator line numbers
tail -n 1 | # take the last one
cut -d ':' -f 1) )) # retain only line number
</input/file tail -n +$(</input/file awk '/^---$/ {n=NR+1} END {print n}')
</input/file tail -c +$(</input/file LC_CTYPE=C awk '
{pos+=length($0 RS)} # pos contains the current byte offset in the file
/^---$/ {last=pos} # last contains the byte offset after the last separator
END {print last+1} # print characters from last (+1 because tail counts from 1)
')
Anexo: Si tiene más de POSIX, aquí hay una versión simple de un paso que se basa en una extensión común a awk que permite que el separador de registros RS
sea una expresión regular (POSIX solo permite un solo carácter). No es completamente correcto: si el archivo termina con un separador de registros, imprime el fragmento antes del último separador de registros en lugar de un registro vacío. La segunda versión que usa RT
evita ese defecto, pero RT
es específica de GNU awk.
awk -vRS='(^|\n)---+($|\n)' 'END{printf $0}'
gawk -vRS='(^|\n)---+($|\n)' 'END{if (RT == "") printf $0}'
Gilles 'SO- deja de ser malvado'
fuente
sed
funciona bien, pero no puedoawk
ejecutar el ejemplo; se cuelga ... y obtengo un error en el 3er ejemplo:cut -f ':' -t 1
... corte: opción no válida - 't'cut
ejemplo. No veo nada malo con elawk
ejemplo, ¿qué versión de awk estás usando y cuál es tu entrada de prueba?awk
versión está trabajando .. es sobresalen teniendo un muy largo tiempo en un archivo de gran tamaño .. lased
versión procesada el mismo archivo en 0.470s .. Mis datos de prueba es muy ponderada ... sólo dos trozos con un solitario '---' tres líneas desde el final de 1 millón de líneas ...Una estrategia de dos pases parece ser lo correcto. En lugar de sed lo usaría
awk(1)
. Los dos pases podrían verse así:para obtener el número de línea. Y luego repita todo el texto que comienza desde ese número de línea con:
Esto no debería requerir un almacenamiento en búfer excesivo.
fuente
awk -v line=$(awk '/^---$/{n=NR}END{print n}' file) 'NR>line' file
Los primeros
sed
números de línea de salida de las líneas "---" ...El segundo
sed
extrae el último número de la salida del primer sed ...Agregue 1 a ese número para obtener el inicio de su bloque "ccc" ...
El tercero salidas 'sed' desde el inicio del bloque "ccc" a EOF
Actualización (con información modificada sobre los métodos de Gilles)
Bueno, me preguntaba cómo se desempeñaría Glenn Jackman
tac
, así que probé las tres respuestas (al momento de escribir) ... El archivo de prueba contenía 1 millón de líneas (de sus propios números de línea).Todas las respuestas hicieron lo que se esperaba ...
Aquí están los tiempos ...
Gilles
sed
(pase único)Gilles
awk
(pase único)Gilles 'dos pasos' (primer método)
Gilles 'dos pasos' (segundo método) ... muy rápido
Gilles 'dos pasos' (tercer método)
Gilles 'gawk' (método RT) ... muy rápido , pero no es POSIX.
Glenn Jackman ... muy rápido , pero no es POSIX.
fred.bear
Mackie Messer
fuente
Utilice " tac " que genera las líneas de un archivo de principio a fin:
fuente
tac
no es POSIX, es específico de Linux (está en GNU coreutils y en algunas instalaciones de busybox).Podrías usar
ed
Cómo funciona:
t
duplica la línea actual (.
), que siempre es la última línea cuando seed
inicia (en caso de que el delimitador esté presente en la última línea),1,?===?d
elimina todas las líneas hasta la coincidencia anterior incluida (ed
aún está en la última línea) ) luego$d
elimina la última línea (duplicada),,p
imprime el búfer de texto (reemplace conw
para editar el archivo en su lugar) y finalmente seq
cierraed
.Si sabe que hay al menos un delimitador en la entrada (y no me importa si también está impreso), entonces
Sería el más corto.
Cómo funciona: agrega todas las líneas al
H
búfer antiguo, sobrescribe elh
búfer antiguo cuando encuentra una coincidencia,d
elige todas las líneas excepto la última$
cuandox
cambia los búferes (y las impresiones automáticas).fuente