Imprimir linha apenas se a linha superior incluir palavra específica

3

Temos o seguinte arquivo com hostnames e host ip's (arquivo longo com 90-100 máquinas por máquina linux)

hosts.cluster.conf

  "href" : "http://localhost:8080/api/v1/hosts/worker02.sys87.com",
  "Hosts" : 
    "cluster_name" : "hdp",
    "host_name" : "worker02.sys87.com",
    "ip" : "23.67.32.65"


  "href" : "http://localhost:8080/api/v1/hosts/worker03.sys87.com",
  "Hosts" : 
    "cluster_name" : "hdp",
    "host_name" : "worker03.sys87.com",
    "ip" : "23.67.32.66"


  "href" : "http://localhost:8080/api/v1/hosts/worker04.sys87.com",
  "Hosts" : 
    "host_name" : "worker04.sys87.com",
    "ip" : "23.67.32.67"


  "href" : "http://localhost:8080/api/v1/hosts/worker05.sys87.com",
  "Hosts" : 
    "cluster_name" : "hdp",
    "host_name" : "worker05.sys87.com",
    "ip" : "23.67.32.68"

queremos imprimir todas as linhas host_name somente se a linha superior antes incluir a palavra " cluster_name "

resultados esperados

"host_name" : "worker02.sys87.com",

"host_name" : "worker03.sys87.com",

"host_name" : "worker05.sys87.com",
    
por yael 07.01.2018 / 13:04

9 respostas

3

Solução awk curta:

awk '/cluster_name/{ cl=NR }/host_name/ && NR-1==cl' hosts.cluster.conf
  • /cluster_name/{ cl=NR } - capturando o número recorde de "cluster_name" line
  • /host_name/ - ao encontrar "host_name" line
  • NR-1==cl - garantindo que o atual "host_name" número de registro NR seja o próximo após "cluster_name" número do registro (apresentado por cl )

A saída:

"host_name" : "worker02.sys87.com",
"host_name" : "worker03.sys87.com",
"host_name" : "worker05.sys87.com",

No caso, se host_name aparecer como a primeira linha, embora eu duvide disso no caso real, use a seguinte versão:

awk '/cluster_name/{ cl=NR }/host_name/ && cl && NR-1==cl' hosts.cluster.conf
    
por 07.01.2018 / 13:23
5
sed '/host_name/!h;//!d;x;/cluster_name/!d;g' infile

salvará cada linha que não corresponder a host_name ao buffer h old e, em seguida, d elete; para cada linha restante, ele irá e x alterar os buffers e, se o espaço padrão não contiver cluster_name , ele será excluído, senão a g e a linha original serão recuperadas do buffer de retenção e da autoprint.

    
por 07.01.2018 / 14:48
3

tente

 awk '/cluster_name/ {p=1 ; next ;} 
 /host_name/ && p { print ; }
 {p=0}' 

Isso basicamente lembra a linha com cluster_name . Se host_name for encontrado, sem cluster_name antes, não será impresso.

Note que todo o código awk pode ser de uma linha.

    
por 07.01.2018 / 13:22
1
#!/usr/bin/perl

$/='';

while(<>) {
 next unless (m/"cluster_name"/);
 chomp;

 foreach my $l (split /\n/) {
    print $l, "\n\n" if ($l =~ m/"host_name"/);
 };
};

Em inglês: Lendo o arquivo um parágrafo por vez ( $/=''; ), pule parágrafos que não contenham a string "cluster_name" . Para aqueles que contêm essa cadeia, percorra cada linha do parágrafo e imprima linhas contendo "host_name" , com duas novas linhas depois de cada instância.

Exemplo de saída:

$ ./extract-hosts.pl hosts.cluster.conf 
    "host_name" : "worker02.sys87.com",

    "host_name" : "worker03.sys87.com",

    "host_name" : "worker05.sys87.com",
    
por 07.01.2018 / 13:22
1

Bem, temos sed e awk , agora é hora de GNU grep !

cat infile | grep --after-context 1 cluster_name | grep host_name

Explicação

O primeiro comando cat lê os dados no canal para processamento. Você pode substituir esse segmento por qualquer comando que envie seu texto de origem para stdout .

O segundo comando encontra qualquer linha com "cluster_name" e imprime a linha seguinte. A saída intermediária é esta:

"cluster_name" : "hdp",
"host_name" : "worker02.sys87.com",
--
"cluster_name" : "hdp",
"host_name" : "worker03.sys87.com",
--
"cluster_name" : "hdp",
"host_name" : "worker05.sys87.com",

Em seguida, o segmento final apenas imprime o conteúdo das linhas com "host_name". Assim, o resultado final é:

"host_name" : "worker02.sys87.com",
"host_name" : "worker03.sys87.com",
"host_name" : "worker05.sys87.com",

Comentários

  1. Nem todo grep tem --before-context parameter. Certifique-se de estar usando o GNU grep e você deve estar bem.
  2. Se este for um idioma como o JSON, seria melhor aprender e usar um analisador de idioma apropriado, como jmespath ou jq .
por 07.01.2018 / 16:35
0

Este snippet:

# Utility functions: print-as-echo, print-line-with-visual-space.
pe() { for _i;do printf "%s" "$_i";done; printf "\n"; }
pl() { pe;pe "-----" ;pe "$*"; }

pl " Input data file $FILE:"
cat $FILE

pl " Expected output:"
cat $E

pl " Results:"
cgrep -D -w '"href"' +w '"ip"' "cluster_name" $FILE |
grep '"host_name"'

produz:

-----
 Input data file data1:
  "href" : "http://localhost:8080/api/v1/hosts/worker02.sys87.com",
  "Hosts" : 
    "cluster_name" : "hdp",
    "host_name" : "worker02.sys87.com",
    "ip" : "23.67.32.65"


  "href" : "http://localhost:8080/api/v1/hosts/worker03.sys87.com",
  "Hosts" : 
    "cluster_name" : "hdp",
    "host_name" : "worker03.sys87.com",
    "ip" : "23.67.32.66"


  "href" : "http://localhost:8080/api/v1/hosts/worker04.sys87.com",
  "Hosts" : 
    "host_name" : "worker04.sys87.com",
    "ip" : "23.67.32.67"


  "href" : "http://localhost:8080/api/v1/hosts/worker05.sys87.com",
  "Hosts" : 
    "cluster_name" : "hdp",
    "host_name" : "worker05.sys87.com",
    "ip" : "23.67.32.68"


  "href" : "http://localhost:8080/api/v1/hosts/worker06.sys87.com",
  "Hosts" : 
    "host_name" : "worker06.sys87.com",
    "cluster_name" : "hdp",
    "ip" : "23.67.32.69"

-----
 Expected output:
"host_name" : "worker02.sys87.com",

"host_name" : "worker03.sys87.com",

"host_name" : "worker05.sys87.com",

-----
 Results:
    "host_name" : "worker02.sys87.com",
    "host_name" : "worker03.sys87.com",
    "host_name" : "worker05.sys87.com",
    "host_name" : "worker06.sys87.com",

Primeiro, extrai todas as sub-rotinas que contêm "cluster_name". Ele faz isso extraindo estrofes que são regiões (ou windows: -w, + w) "href" .. "ip". A partir desse subconjunto, um grep normal extrai as linhas desejadas correspondentes a "host_name".

Esse esquema permite que as linhas "cluster_name" e "host_name" apareçam em qualquer lugar da sub-rotina, mesmo em posições relativas diferentes, como na sub-rotina adicional "worker06".

Feito em um sistema como:

OS, ker|rel, machine: Linux, 3.16.0-4-amd64, x86_64
Distribution        : Debian 8.9 (jessie) 
bash GNU bash 4.3.30
cgrep ATT cgrep 8.15

Alguns detalhes para o cgrep:

cgrep   shows context of matching patterns found in files (man)
Path    : ~/executable/cgrep
Version : 8.15
Type    : ELF 64-bit LSB executable, x86-64, version 1 (SYS ...)
Home    : http://sourceforge.net/projects/cgrep/ (doc)

Felicidades ... felicidades, drl

    
por 07.01.2018 / 14:15
0

Outro sed :

 sed -n '/host_name/ { x; /cluster_name/ { x; p; x }; x }; h' file
    
por 07.01.2018 / 14:29
0

Aqui está outra solução sed , que talvez seja mais fácil de entender que as outras:

sed -n '/cluster_name/{n;/host_name/p}'

Aqui, procuramos apenas uma linha que corresponda a cluster_name e, se a próxima linha corresponder a host_name , imprimimos.

    
por 07.01.2018 / 17:25
0

Abaixo o comando sed onliner faz o mesmo.Testou bem o seu trabalho

sed -n '/cluster_name/,+1p' hosts.cluster.conf | sed -n '/host_name/p'

saída

  "host_name" : "worker02.sys87.com",
    "host_name" : "worker03.sys87.com",
    "host_name" : "worker05.sys87.com",
    
por 07.01.2018 / 17:42