Por que não regex '+' trabalho como esperado? [duplicado]

3
[root@localhost opt]# cat cfg
key = value
[root@localhost opt]# grep 'key\s*=\s*.+' cfg
[root@localhost opt]# 

Minha intenção é: o sinal = pode ser seguido por zero ou mais espaços, mas deve ser seguido por um ou mais caracteres não espaciais.

Por que não sai a linha key = value ?

    
por xmllmx 12.08.2016 / 04:59

2 respostas

10

Observe:

$ grep 'key\s*=\s*.+' cfg
$ grep 'key\s*=\s*.\+' cfg
key = value
$ grep -E 'key\s*=\s*.+' cfg
key = value

Em Basic Regular Expressions (BRE, o padrão), + significa um sinal de mais. Como uma extensão GNU, pode-se sinalizar um ou mais dos caracteres anteriores usando \+ . Isso também é verdade para ? , { , | e ( . A menos que tenha escapado com uma barra invertida, todos eles são tratados como um caractere comum no BRE.

As regras mudam se você usar Expressões regulares estendidas, -E . Para ERE, a barra invertida não é necessária e um + simples significa um ou mais caracteres anteriores. Em ERE, \+ significa um sinal de mais normal normal.

    
por 12.08.2016 / 05:06
1
key\s*=\s*.+

é a sintaxe GNU ERE (supondo que você queira que \s corresponda a qualquer caractere de espaçamento e + para corresponder a um ou mais dos átomos anteriores), então você precisaria da implementação GNU de grep e passar -E opção.

No entanto, mesmo assim, não faria muito sentido

Primeiro

grep 'key\s*=\s*.+'

é funcionalmente equivalente a

grep 'key\s*=\s*.'

Porque, se uma string corresponder a anything.+ , ela também corresponderá a anything. e vice-versa.

Além disso, um caractere de espaçamento também é um caractere. Como \s* corresponde a 0 ou mais caracteres de espaçamento, key\s*=\s*. é funcionalmente equivalente a key\s*=. (linhas que contêm key<optional-spaces>=<one-character-space-or-not> ).

Aqui você quer:

grep 'key\s*=\s*\S'

para pedir pelo menos um caractere sem espaçamento a ser encontrado à direita do = , que é funcionalmente equivalente a:

grep 'key\s*=.*\S'

Observe que ele corresponde a key = foo , mas também nonkey = foo . Se você quiser que o key seja encontrado apenas no início da linha, será necessário solicitá-lo com o ^ anchor:

grep '^key\s*=.*\S'

Ou use -x para o regexp corresponder à linha inteira:

grep -x 'key\s*=.*\S.*'

Observe que o equivalente padrão de \s é [[:space:]] ( [^[:space:]] para \S ).

Outra maneira de abordar o requisito seria usar operadores estendidos encontrados em alguns regexps, como os PCRE, para evitar o back-tracking.

key=\s*. corresponde a key=  porque o mecanismo regexp tem \s* avidamente pelos caracteres de espaço após o = , localiza 1 e, em seguida, percebe que não pode corresponder ao . ao atingir o final de a linha e, em seguida, rastreia para tentar com menos correspondências de \s (0 nesse caso) para que o próximo . possa corresponder (aqui um caractere de espaço).

Com o PCRE, como quando usamos a opção -P com o GNU grep , você pode escrever:

 grep -P '^key\s*=(?>\s*).'

Essa sintaxe (?>...) impede o rastreamento de retorno. Portanto, o \s* irá consumir o maior número possível de caracteres de espaçamento, sem poder retroceder, portanto, somente coincidirá se houver pelo menos um caractere sem espaçamento após os espaços.

$ printf 'key=%s\n' '' ' ' ' a' | grep '^key\s*=\s*.'
key=
key= a
$ printf 'key=%s\n' '' ' ' ' a' | grep -P '^key\s*=(?>\s*).'
key= a
$ printf 'key=%s\n' '' ' ' ' a' | grep '^key\s*=.*\S'
key= a
    
por 12.08.2016 / 14:43