PCRE-regex Use grep para excluir um grupo de captura

7

Estou usando GNU grep com o suporte a -P PCRE Regex para corresponder sequências de caracteres de um arquivo. O arquivo de entrada possui linhas contendo strings como:

FOO_1BAR.zoo.2.someString:More-RandomString (string here too): 0.45654343

Eu quero capturar os números 2 e 0.45654343 da linha acima. Eu usei um regEx

grep -Po ".zoo.\K[\d+](.*):\ (.*)$" file

Mas isso está me produzindo um resultado como

2.someString:More-RandomString (string here too): 0.45654343

Eu posso obter o primeiro número do primeiro grupo de captura como 2 e também corresponder a um grupo de captura no final da linha. Mas eu não sou capaz de pular as palavras / linhas entre dois grupos de captura.

Eu sei que tenho um grupo (.*) que está capturando essas palavras no meio. O que eu tentei fazer é incluir outro \K para ignorá-lo como

grep -Po ".zoo.\K[\d+](.*):\K (.*)$" file

Mas isso me deu apenas o segundo grupo de captura como 0.556984 .

Também com um grupo sem captura com a sintaxe (?:) como

grep -Po ".zoo.\K[\d+](?=.someString:More-RandomString (string here too)):\ (.*)$"

Mas isso não me deu nada. O que estou perdendo aqui?

    
por Inian 28.11.2016 / 10:26

1 resposta

8
O nome de

grep vem depois do comando g/re/p ed . Seu objetivo principal é imprimir as linhas que correspondem a um regexp. Não é seu papel editar o conteúdo dessas linhas. Você tem sed (o editor de fluxo) ou awk para isso.

Agora, algumas implementações de grep , começando com GNU grep , adicionaram uma opção -o para imprimir a parte correspondente de cada linha (o que é correspondido pelo regexp, não por seus grupos de captura). Você tem alguma implementação de grep como o GNU novamente (com -P ) ou pcregrep que suportam PCREs para seus regexps.

pcregrep na verdade adicionou uma opção -o<n> para imprimir o conteúdo de um grupo de captura. Então você poderia fazer:

pcregrep -o1 -o2 --om-separator=' ' '.zoo.(\d+).*:\s+(.*)'

Mas aqui, a solução padrão óbvia é usar sed :

sed -n 's/^.*\.zoo\.\([0-9]\{1,\}\).*:[[:space:]]\{1,\}/ /p'

Ou se você quiser regexps perl, use perl:

perl -lne 'print "$1 $2" if /\.zoo\.(\d+).*:\s+(.*)/'

Com o GNU grep , se você não se importa que as correspondências apareçam em linhas diferentes, você pode fazer:

$ grep -Po '\.zoo\.\K\d+|:\s+\K.*' < file
2
0.45654343

Observe que, embora \K redefina o início da parte correspondente, isso não significa que você pode se safar com as duas partes da sobreposição da alternância.

grep -Po '.zoo.(\K\d+|.: \K.)'

não funcionaria, assim como echo foobar | grep -Po 'foo|foob' não funcionaria (ao imprimir foo e foob ). foo|foob corresponde pela primeira vez foo e, em seguida, grep procura outras possíveis correspondências na entrada após o foo , começando assim em b de bar , por isso não é possível encontrar mais depois disso. / p>

Acima, com grep -Po '\.zoo\.\K\d+|:\s+\K.*' , procuramos apenas :<spaces><anything> na segunda parte da alternância. Isso corresponde na parte que está após .zoo.<digits> , mas isso também significa que eles encontrarão esses :<spaces><anything> em qualquer lugar na entrada, não apenas quando eles seguirem .zoo.<digits> .

Existe uma maneira de contornar isso, usando outro operador especial PCRE: \G . \G corresponde no início do assunto. Para uma única correspondência, isso equivale a ^ , mas com várias correspondências (pense em sed / perl g sinalizador em s/.../.../g ) como com -o onde grep tenta encontrar todos as partidas na linha, que também coincidem após o final da partida anterior. Então, se você fizer isso:

grep -Po '\.zoo\.\K\d+|(?!^)\G.*:\s+\K.*'

Em que (?!^) é um operador de look-ahead negativo que significa não no início da linha , que \G corresponderá somente após uma correspondência anterior bem-sucedida (não vazia) .*:\s+\K.* corresponderá apenas se seguir uma correspondência anterior com sucesso, e isso só pode ser o .foo.<digits> um, já que a outra parte da alternação corresponde ao final da linha.

Em uma entrada como:

.zoo.1.zoo.2 tar: blah

Isso produziria:

1
2
blah

Embora. Se você não quisesse, também desejaria que a primeira parte da alternação correspondesse apenas no início da linha. Algo como

grep -Po '^.*?\.zoo\.\K\d+|(?!^)\G.*:\s+\K.*'

Isso ainda gera 2 em uma entrada como .zoo.2 no colon character ou .zoo.2 blah: . Que você poderia contornar com um operador de look-ahead na primeira parte da alternância, e procurar pelo menos um não-espaço após :<spaces> (e também usando $ para evitar problemas com não-caracteres)

grep -Po '^.*?\.zoo\.\K\d+(?=.*:\s+\S.*$)|(?!^)\G.*:\s+\K\S.*$'

Você provavelmente precisaria de algumas páginas de comentários para explicar esse regexp, então eu ainda iria para a linha reta sed / perl solutions ...

    
por 28.11.2016 / 10:49