Bash: como obter algumas linhas de um arquivo e salvar a saída em outro arquivo [fechado]

Jan 17 2021

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

dawg Jan 17 2021 at 22:29

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 awkvocê 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
WilliamPursell Jan 17 2021 at 17:57
$ 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.

potong Jan 17 2021 at 18:07

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.

IdrissNeumann Jan 17 2021 at 18:15

À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, sorte uniqnã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.

Enlico Jan 17 2021 at 18:47

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
EdMorton Jan 17 2021 at 22:50

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.