Bash: come ottenere alcune righe da un file e salvare l'output in un altro file [chiuso]
Ho un file di registro come questo
$ 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)) ####
Come generare un nuovo registro analizzato in modo che l'output del nuovo registro file sia simile a questo:
$ 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)) ####
Come ottenere solo l'ultimo progresso [ 60% 60920/101076]
fino alla fine del file, usando forse grep, sed o altro. Grazie
Risposte
Ecco un perl:
$ perl -0777 -lne 'print $1 if /(^\[[^[]*\z)/m' file
O una pipa in perl:
$ perl -E 'say reverse <>' file | perl -lpE 'if (/^\[/){ say; last}' | perl -E 'say reverse <>'
Per un awk
puoi fare:
$ awk 'BEGIN{RS="\\["}END{print "[" $0}' file
Certo, potresti sapere che se l'errore è sempre di 3 righe, il più semplice è solo usare tail
:
$ tail -n 3 file
Tutte le stampe:
[ 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
è lo strumento giusto per questo genere di cose. Qui, controlliamo se il 2 ° campo corrisponde al 2 ° campo della riga precedente e stampiamo se lo fa. Quindi memorizzare la riga precedente e ripetere. Stampa sempre l'ultima riga dell'input.
Questo potrebbe funzionare per te (GNU sed):
sed '/^\[/h;//!H;$!d;x' file
Se una riga inizia, [
memorizzala nello spazio di attesa (sovrascrivendo qualsiasi cosa precedente).
Altrimenti, aggiungi la riga corrente allo spazio di attesa.
Elimina tutte le righe tranne l'ultima.
Alla fine del file, passare allo spazio di attesa e stampare il suo contenuto.
A volte non sei fortunato se vuoi eseguire questo tipo di filtro senza dover cambiare l'ordine delle righe. E se quelle linee non sono scritti all'inizio del file, o alla fine: tac
, sort
e uniq
non sarebbero gli strumenti giusti.
Ecco una soluzione che utilizza 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 versione leggibile e spiegata:
# 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 differenza con le soluzioni William Pursel è quando le tue percentuali aumentano. Vediamo la differenza di comportamento in questo 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)) ####
Quindi potresti scegliere se vuoi solo mantenere l'ultima linea di progressione anche se la percentuale non è la stessa o se vuoi solo filtrare le linee che hanno la stessa percentuale più volte.
Se è garantito che la prima riga che desideri stampare è l'ultima riga che inizia con [
, puoi farlo, che essenzialmente sostituisce tutto dall'inizio del file all'ultimo [
preceduto da un'interruzione di riga con un singolo [
:
sed -z 's/.*\n\[/[/' file
Supponendo che la ...
riga all'inizio del tuo input di esempio rappresenti più [...] ...
linee iniziali :
$ 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)) ####
Se questa ipotesi è sbagliata, modifica la tua domanda per mostrare un esempio minimo, completo e verificabile, uno con solo valori rappresentativi, non ...
s.