Bash: como obter algumas linhas de um arquivo e salvar a saída em outro arquivo [fechado]
Eu tenho um arquivo de log 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)) ####
Como gerar novos registros analisados para que a saída do novo registro de arquivo seja assim:
$ 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 obter apenas o último progresso [ 60% 60920/101076]
até o final do arquivo, usando talvez grep, sed ou qualquer coisa. Obrigada
Respostas
Aqui está um perl:
$ perl -0777 -lne 'print $1 if /(^\[[^[]*\z)/m' file
Ou um tubo perl:
$ perl -E 'say reverse <>' file | perl -lpE 'if (/^\[/){ say; last}' | perl -E 'say reverse <>'
Por um awk
você pode fazer:
$ awk 'BEGIN{RS="\\["}END{print "[" $0}' file
Claro, você deve saber que se a falha for sempre de 3 linhas, o mais simples é apenas usar tail
:
$ tail -n 3 file
Impressão completa:
[ 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
é a ferramenta certa para esse tipo de coisa. Aqui, verificamos se o 2º campo corresponde ao 2º campo da linha anterior e imprimimos. Em seguida, armazene a linha anterior e repita. Sempre imprima a última linha da entrada.
Isso pode funcionar para você (GNU sed):
sed '/^\[/h;//!H;$!d;x' file
Se uma linha começar, [
armazene-a no espaço de retenção (sobrescrevendo tudo o que havia anteriormente).
Caso contrário, acrescente a linha atual ao espaço de espera.
Exclua todas as linhas, exceto a última.
No final do arquivo, alterne para o espaço de retenção e imprima seu conteúdo.
Às vezes, você não tem sorte se deseja realizar esse tipo de filtro sem ter que alterar a ordem das linhas. E se essas linhas não estão escritos no início do seu arquivo, ou no final: tac
, sort
e uniq
não seria as ferramentas certas.
Aqui está uma solução 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)) ####
A versão legível e 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]
}
}
}
A diferença com as soluções William Pursel é quando seus percentuais estão aumentando. Vamos ver a diferença de comportamento neste 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)) ####
Portanto, você pode escolher se deseja apenas manter a última linha de progressão, mesmo que a porcentagem não seja a mesma, ou se deseja apenas filtrar as linhas que têm a mesma porcentagem várias vezes.
Se for garantido que a primeira linha que você deseja produzir é a última linha começando com [
, então você pode fazer isso, o que essencialmente substitui tudo do início do arquivo ao último [
precedido por uma quebra de linha por um único [
:
sed -z 's/.*\n\[/[/' file
Supondo que a ...
linha no início de sua entrada de amostra represente mais [...] ...
linhas principais :
$ 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 essa suposição estiver errada, edite sua pergunta para mostrar um exemplo mínimo, completo e verificável - um com apenas valores representativos, não ...
s.