Selecione linhas com base nas linhas acima delas

0

Eu tenho uma lista de itens, dos quais desejo selecionar os nomes dos itens ativos:

item {
  status: "Active"
  properties {
    key_a: value
  }
  id: 42
  name: "Foo"
}
item {
  status: "Disabled"
  properties {
    key_b: value
  }
  id: 12
  name: "Bar"
}
item {
  status: "Active"
  id: 2
  name: "Baz"
}

Eu sei que posso extrair os nomes usando grupos de captura com pcregrep :

$ cat list.txt | pcregrep -o1 -i '^  name: "(.*)"'
Foo
Bar
Baz

Usando uma expressão OR, também posso obter uma lista de valores e nomes de status repetidos:

$ cat list.txt | pcregrep -o2 -i '^  (status|name): "(.*)"'
Active
Foo
Disabled
Bar
Active
Baz

Por fim, preciso filtrar os nomes na lista com base nas linhas anteriores. Como posso fazer isso?

O resultado final deve ser:

Foo
Baz
    
por danijar 10.08.2018 / 18:36

4 respostas

1

Como a maior parte do trabalho pesado já foi feito por pcregrep , agora você pode passar o seu o / p para este pequeno snippet sed :

  sed -ne 'N;s/^Active\n//p'

que faz com que sed considere 2 linhas de cada vez, em vez do padrão 1. O comando N cola a próxima linha no espaço de padrão, separando com uma nova linha \n . Agora, somente se sed conseguir remover a primeira linha ativa no espaço de padrão, o espaço de padrão restante será impresso. Esta é uma impressão condicional. Otw nothing e -n devem garantir que não haja impressão automática do espaço padrão. HTH.

    
por 11.08.2018 / 19:11
2

Eu não acho que você pode fazer isso com uma variação grep sozinha (reconhecidamente não sei pcregrep ). Experimente awk :

awk '/^ *status.*Active.$/ {ACT = 1} /^ *name:/ && ACT {gsub (/"/, "", $2); print $2; ACT = 0}' file
Foo
Baz
    
por 10.08.2018 / 19:04
1

Você também pode usar o sed

sed '/status.*Active/,/name/!d;/name/!d;s/[^"]*"\([^"]*\)"//' infile
    
por 10.08.2018 / 20:32
1

Você também pode usar o operador de intervalo de Perl e restringi-lo com um boolean condition para lidar com parênteses aninhados {} em um bloco.

Normalmente, uma pessoa gravaria um intervalo em Perl as /re1/ ... /re2/ , isso fará com que perl selecione os blocos que começam com regex /re1/ e terminem nas linhas que satisfazem a regex /re2/ . Poderíamos restringir ainda mais isso, digamos: /re1/ ... /re2/ && $depth==0 .

Isso fará com que perl selecione apenas os blocos que possuem uma restrição adicional da profundidade sendo zero. Como neste caso, a finalização do bloco acontece somente quando o } é encontrado e faz com que a contagem de profundidade caia para zero, OTW, a acumulação de blocos continua além dessa marca também.

perl -lne '
    if ( /\{/ ... /\}/ && !$depth ) {
        if    ( /\{/ )                         { $depth = /^\h*item\h+\{\h*$/ ? 0 : ++$depth;     }
        elsif ( /\}/ )                         { print($name),undef($flag) if !$depth-- && $flag; }
        elsif ( /^\h*status:\h*"Active"\h*$/ ) { $flag = 1;                                       }
        elsif ( /^\h*name:\h/ )                { $name = (split /"/)[1];                          }
    }
' input.file
    
por 12.08.2018 / 13:20