Como posso incluir informações na linha acima ao procurar por um padrão em uma linha?

1

Eu preciso diferenciar entre a primeira imagem e os segundos cenários de imagem ao recuperar informações usando o grep. Ambos são created_at mas um é para imagem e um é para tweet. Todos os tweets têm um }, na linha acima, então achei que poderia usar essa informação, mas não sei como fazer isso.

Aqui está o grep que eu uso:

grep -wirnE 'Wed Oct 19 2(1:[0-5][0-9]:[0-5][0-9]|2:([0-2][0-9]:[0-5][0-9]|30:00)) .* 2016' *

    
por Mona Jalal 27.09.2017 / 08:09

2 respostas

2

Você pode usar as opções -A1 e -B1 para deixar grep imprimir a linha após (-A) e antes (-B) a linha correspondente. Tente a seguinte linha de comando,

grep -B1 created_at log-file|grep -A1 '^}'|grep created_at

Eu testei com o seguinte arquivo de entrada chamado log-file

asdf
qwerty
...
},
"created_at" "date-with-near-}"
zxcv
some other string
"created_at" "date-without-}"
...

Sequência de testes

$ grep -B1 created_at log-file
},
"created_at" "date-with-near-}"
--
some other string
"created_at" "date-without-}"

$ grep -B1 created_at log-file|grep -A1 '^}'
},
"created_at" "date-with-near-}"

$ grep -B1 created_at log-file|grep -A1 '^}'|grep created_at
"created_at" "date-with-near-}"
    
por sudodus 27.09.2017 / 08:51
2

Você pode usar o comando sed N para ler várias linhas no espaço padrão.

Para encontrar o primeiro:

sed -nr '/\}/N; /.*\}.*\n.*"Wed Oct 19 .* 2016/Ip' file

e para remover a linha anterior:

sed -nr '/}/N; s/.*}.*\n(.*"Wed Oct 19 .* 2016)//Ip' file

O problema é que sed não informa de qual arquivo a linha é, e não possui um sinalizador de pesquisa de arquivo recursivo (afaik). Isso pode ser conseguido ativando a globalização recursiva com ** no shell (mas o problema "de qual arquivo veio?" Permanece):

shopt -s globstar
sed -nrs '/}/N; s/.*}.*\n(.*"Wed Oct 19 .* 2016)//Ip' **

Com vários arquivos, adicione o sinal -s para que sed considere o fluxo como arquivos separados (para evitar correspondências indesejadas de várias linhas) Você pode adicionar sua expressão detalhada no meio ...

sed -nrs '/}/N; s/.*}.*\n(.*"Wed Oct 19 2(1:[0-5][0-9]:[0-5][0-9]|2:([0-2][0-9]:[0-5][0-9]|30:00)) .* 2016)//Ip' **

Para a segunda ocorrência sem } na linha anterior

sed -nr '/^[^}]*$/N; /.*\n.*"Wed Oct 19 .* 2016/Ip' file

e removendo a linha anterior:

sed -nr '/^[^}]*$/N; s/.*\n(.*"Wed Oct 19 .* 2016)//Ip' file

Para combinar isso em algo mais útil:

for f in **; do [[ -f "$f" ]] && echo -e ""$f":\n tweet: $(sed -nr '/}/N; s/.*}.*\n(.*"Wed Oct 19 .* 2016)//Ip' "$f")\n image: $(sed -nr '/^[^}]*$/N; s/.*\n(.*"Wed Oct 19 .* 2016)//Ip' "$f")"; done 

ou ... ligeiramente mais legível (!)

#!/bin/bash
shopt -s globstar

for f in **; do 
   [[ -f "$f" ]] && 
   echo -e ""$f":\n tweet: $(sed -nr '/}/N; s/.*}.*\n(.*"Wed Oct 19 .* 2016)//Ip' "$f")"
done 

Isto dá saída parecida com:

file1:
 tweet: "created_at": "Wed Oct 19 12:36:54 +0000 2016"
 image: "created_at": "Wed Oct 19 somethingsomething 2016"
file2:
 tweet: "created_at": "Wed Oct 19 random-chars 2016"
 image: "created_at": "Wed Oct 19 whatever 2016"

Se você quiser excluir um ou outro, remova a parte relevante do script, por exemplo, para obter apenas o tweet ...

for f in **; do 
   [[ -f "$f" ]] && 
   echo -e ""$f":\n tweet: $(sed -nr '/}/N; s/.*}.*\n(.*"Wed Oct 19 .* 2016)//Ip' "$f")"
done

Notas

  • sed -n fica quieto até que solicitemos saída - isso é usado em combinação com o comando p print para imitar a ação de grep
  • -r usa regex estendido
  • /}/N encontra uma linha com } e lê a próxima linha no espaço padrão
  • /^[^}]*$/N encontra uma linha sem } e lê a próxima linha no espaço padrão
  • I procura insensível a maiúsculas
  • p imprime as linhas encontradas / editadas
  • s/old/new replace old com new
por Zanna 27.09.2017 / 09:00