Bash: Cómo obtener algunas líneas de un archivo y guardar la salida en otro archivo [cerrado]
Tengo un archivo de registro como este
$ cat build.log
..........
[ 60% 60917/101076] AAPT2 compile ....
[ 60% 60918/101076] AAPT2 compile ....
[ 60% 60919/101076] AAPT2 compile ....
[ 60% 60920/101076] AAPT2 compile ....
ninja: build stopped: subcommand failed.
21:41:22 ninja failed with: exit status 1
#### failed to build some targets (17:26 (mm:ss)) ####
Cómo generar como nuevos registros analizados para que la salida del nuevo registro de archivos sea así:
$ cat parsed.log
[ 60% 60920/101076] AAPT2 compile ....
ninja: build stopped: subcommand failed.
21:41:22 ninja failed with: exit status 1
#### failed to build some targets (17:26 (mm:ss)) ####
Como solo obtener el último progreso [ 60% 60920/101076]
hasta el final del archivo, usando tal vez grep, sed o cualquier cosa. Gracias
Respuestas
Aquí hay una perl:
$ perl -0777 -lne 'print $1 if /(^\[[^[]*\z)/m' file
O una pipa de perl:
$ perl -E 'say reverse <>' file | perl -lpE 'if (/^\[/){ say; last}' | perl -E 'say reverse <>'
Por un awk
puedes hacer:
$ awk 'BEGIN{RS="\\["}END{print "[" $0}' file
Por supuesto, puede saber que si la falla es siempre de 3 líneas, la más simple es usar tail
:
$ tail -n 3 file
Todo impreso:
[ 60% 60920/101076] AAPT2 compile ....
ninja: build stopped: subcommand failed.
21:41:22 ninja failed with: exit status 1
$ cat build.log ........ [ 60% 60917/101076] AAPT2 compile .... [ 60% 60918/101076] AAPT2 compile .... [ 60% 60919/101076] AAPT2 compile .... [ 60% 60920/101076] AAPT2 compile .... ninja: build stopped: subcommand failed. 21:41:22 ninja failed with: exit status 1 $ awk '$2 != n[2]{print p} {p = $0; split(p,n,FS)} END{print p}' build.log
........
[ 60% 60920/101076] AAPT2 compile ....
ninja: build stopped: subcommand failed.
21:41:22 ninja failed with: exit status 1
awk
es la herramienta adecuada para este tipo de cosas. Aquí, verificamos si el segundo campo coincide con el segundo campo de la línea anterior e imprimimos si lo hace. Luego guarde la línea anterior y repita. Imprima siempre la última línea de la entrada.
Esto podría funcionar para usted (GNU sed):
sed '/^\[/h;//!H;$!d;x' file
Si comienza una línea, [
guárdela en el espacio de espera (sobrescribiendo cualquier cosa que estuviera allí).
De lo contrario, agregue la línea actual al espacio de espera.
Elimina todas las líneas excepto la última.
Al final del archivo, cambie al espacio de espera e imprima su contenido.
A veces no tienes suerte si quieres realizar este tipo de filtro sin tener que cambiar el orden de las líneas. Y si esas líneas no están escritos en el principio del archivo, o al final: tac
, sort
y uniq
no serían las herramientas adecuadas.
Aquí hay una solución usando awk
:
$ awk 'function push(a,e) { a[length(a)+1] = e } BEGIN {split("", lines); to_replace="toreplace"; exists=0} {if ($0 ~ "^\\[ [0-9]+%"){ll=$0; if (exists <= 0) {exists++; push(lines,to_replace)}} else {push(lines, $0)}} END {for (e in lines){if (lines[e] == to_replace) {print ll} else {print lines[e]}}}' test.log
..........
[ 60% 60920/101076] AAPT2 compile ....
ninja: build stopped: subcommand failed.
21:41:22 ninja failed with: exit status 1
#### failed to build some targets (17:26 (mm:ss)) ####
La versión legible y explicada:
# a function to append an element to an array dynamically
function push(a,e) {
a[length(a)+1] = e
}
BEGIN {
split("", lines); # initializing an array
to_replace="toreplace"; # you can change the replace key if you want
exists=0
}
{
if ($0 ~ "^\\[ [0-9]+%"){ # matching all percentages/progression lines, regardless of their values ll=$0;
if (exists <= 0) {
exists++;
push(lines, to_replace)
}
} else {
push(lines, $0)
}
}
END {
for (e in lines) {
if (lines[e] == to_replace) {
print ll
} else {
print lines[e]
}
}
}
La diferencia con las soluciones de William Pursel es cuando sus porcentajes aumentan. Veamos la diferencia de comportamiento en este caso:
$ cat test.log
..........
[ 60% 60917/101076] AAPT2 compile ....
[ 60% 60918/101076] AAPT2 compile ....
[ 60% 60919/101076] AAPT2 compile ....
[ 61% 60920/101076] AAPT2 compile ....
ninja: build stopped: subcommand failed.
21:41:22 ninja failed with: exit status 1
#### failed to build some targets (17:26 (mm:ss)) ####
$ awk 'function push(a,e) { a[length(a)+1] = e } BEGIN {split("", lines); to_replace="toreplace"; exists=0} {if ($0 ~ "^\\[ [0-9]+%"){ll=$0; if (exists <= 0) {exists++; push(lines,to_replace)}} else {push(lines, $0)}} END {for (e in lines){if (lines[e] == to_replace) {print ll} else {print lines[e]}}}' test.log
..........
[ 61% 60920/101076] AAPT2 compile ....
ninja: build stopped: subcommand failed.
21:41:22 ninja failed with: exit status 1
#### failed to build some targets (17:26 (mm:ss)) ####
$ awk '$2 != n[2]{print p} {p = $0; split(p,n,FS)} END{print p}' test.log
..........
[ 60% 60919/101076] AAPT2 compile ....
[ 61% 60920/101076] AAPT2 compile ....
ninja: build stopped: subcommand failed.
21:41:22 ninja failed with: exit status 1
#### failed to build some targets (17:26 (mm:ss)) ####
Por lo tanto, es posible que desee elegir si solo desea mantener la última línea de progresión incluso si el porcentaje no es el mismo o si solo desea filtrar las líneas que tienen el mismo porcentaje varias veces.
Si está garantizado que la primera línea con la que desea salir es la última línea que comienza con [
, entonces puede hacer esto, que esencialmente reemplaza todo desde el principio del archivo hasta la última [
precedida por un salto de línea con un solo [
:
sed -z 's/.*\n\[/[/' file
Suponiendo que la ...
línea al comienzo de la entrada de muestra representa más [...] ...
líneas principales :
$ awk '/^\[/{p=$0 ORS; next} {print p $0; p=""}' build.log
[ 60% 60920/101076] AAPT2 compile ....
ninja: build stopped: subcommand failed.
21:41:22 ninja failed with: exit status 1
#### failed to build some targets (17:26 (mm:ss)) ####
Si esa suposición es incorrecta, edite su pregunta para mostrar un ejemplo mínimo, completo y verificable, uno con solo valores representativos, no ...
s.