Como pesquisar e imprimir o valor correspondente da correspondência na linha de comando?

4

eu tenho abaixo da string, por exemplo,

2017-01-19:31:51 [ABCD:] 37723 - MATCH: 10 [text]

Eu quero encontrar MATCH e imprimir seu valor, 10, usando awk . Eu posso fazer isso usando o tradicional grep e cut , mas quero encontrar o caminho usando sed ou awk .

MATCH pode estar em qualquer posição na linha.

    
por Rocky86 23.01.2017 / 11:24

2 respostas

9

sed -n 's/.* MATCH: \([^ ]*\).*//p'

Imprimiria a sequência de caracteres não espaciais que seguem a ocorrência mais à direita de " MATCH: " em todas as linhas correspondentes.

-n informa sed para não imprimir o espaço padrão por padrão. E o p sinalizador para o comando s informa sed para imprimir o espaço padrão (portanto, o resultado da substituição) se a substituição for bem-sucedida.

Então o:

sed -n 's/pattern/replacement/p'

é um idioma comum para imprimir o resultado de substituições bem-sucedidas.

Note que o acima assume que a entrada é um texto válido. Como .* corresponde a qualquer sequência de caracteres , não corresponderá a sequências de bytes que não formam caracteres válidos. Isso normalmente acontece em locales UTF-8 ao processar texto em outra codificação. Se você estiver em tal caso, talvez queira prefixar essa linha acima com LC_ALL=C . Isso faz com que sed trate cada byte como um caractere, então não há sequências de bytes inválidas possíveis. Isso funcionaria aqui, pois os caracteres que estamos combinando são todos do conjunto de caracteres portátil.

O padrão awk não tem nada equivalente, pois não suporta grupos de captura (o \(...\) capturado em ) em sua função sub() .

Lá, você precisa recorrer à função match() :

awk 'match($0, / MATCH: [^ ]*/) {
       print substr($0, RSTART+8, RLENGTH-8)}'

Ou use truques como:

awk -F ' MATCH: ' 'NF>1 {sub(/ .*/, "", $2); print $2}'

(cuidado com aqueles que considerariam a ocorrência mais à esquerda de " MATCH: " ).

O GNU awk tem uma função gensub() que possui uma funcionalidade semelhante ao comando sed ' s , mas um erro de design, pois não informa se alguma substituição foi feita. Aqui você poderia fazer:

 gawk '(replacement = gensub(/.* MATCH: ([^ ]*).*/, "\1", 1)) != $0 {
   print replacement}'
    
por 23.01.2017 / 11:42
3

Considerando-se que todas as linhas são formatadas da mesma forma (ou pelo menos todas as linhas que contêm MATCH: ), parece que MATCH: é o 5º elemento da linha e o valor que você deseja é o 6º. .

Portanto, no awk você só precisa testar se o quinto elemento é igual a MATCH: e imprimir o 6º elemento da linha, se estiver correto.

$ echo "2017-01-19:31:51 [ABCD:] 37723 - MATCH: 10 [text]" |awk -e '{ if ($5 == "MATCH:") print $6 }' 
    10

EDIT: dada a suposição MATCH: pode estar em qualquer lugar na linha:

  $ echo "2017-01-19:31:51 [ABCD:] 37723 - MATCH: 10 [text]" |awk -e '{ for (x=1; x<NF; x++ ) { if ($x == "MATCH:") {x=x+1; printf("%s\n", $x); break}}}' 
10

Pode não ser muito elegante, mas você precisa percorrer todos os campos da linha e testar cada campo, o que é feito com um loop for e um teste if . Se o campo de teste estiver correspondendo, imprima o próximo campo.

Acabei de adicionar uma pausa para pular diretamente para a próxima linha e continuar a iteração de campo atual.

Em um arquivo de várias linhas:

$ cat terst 
2017-01-19:31:51 [ABCD:] 37723 - MATCH: 10 [text]
2017-01-19:31:51 [ABCD:] 37723 - MATCH: 11 [text]
2017-01-19:31:51 [ABCD:] 37723 - [text]
2017-01-19:31:51 37723 - MATCH: 12 [text]
$ awk -e '{ for (x=1; x<NF; x++ ) { if ($x == "MATCH:") {x=x+1; printf("%s\n", $x); break}}}' terst 
10
11
12
    
por 23.01.2017 / 11:32