grep corresponde a todas as ocorrências de várias expressões regulares

0

Eu preciso extrair todas as variáveis do host de uma instrução SQL que fazem parte de uma concatenação. Exemplo de entrada:

select * from table where :first-var || :second-var

A partir disso eu preciso extrair 'first-var' e 'second-var'.

A correspondência de um ou de outro pode ser feita usando:

grep -o -E ':\S+\s+\|\|'

corresponde a ': first-var ||' e

grep  -o -E '\|\|\s+:\S+'

corresponde a '|| : second-var '

No entanto, quando eu combino essas duas expressões em um padrão de alternância, apenas um resultado é retornado:

grep -o -E '\|\|\s+:\S+|:\S+\s+\|\|'

A divisão do comando em vários padrões também corresponde a apenas um resultado:

grep -o -E -e '\|\| :second-var' -e ':first-var \|\|'

Eu suspeito de que os símbolos de pipe são "usados" após a primeira correspondência porque o seguinte retorna os dois resultados:

grep -o -E -e '\| :second-var' -e ':first-var \|'

Como posso obter todas as correspondências? Note que os símbolos de concatenação não precisam fazer parte da saída, estou interessado apenas em 'first-var' e 'second-var' neste exemplo.

    
por Bram 04.09.2017 / 17:02

3 respostas

3

O problema é que depois de encontrar :first-var || , não há || :second-var restante no texto restante. grep -o só pode imprimir partes das linhas que não se sobrepõem.

Você poderia fazer:

$ perl -lne 'print for /:\S+\s+\|\|/g, /\|\|\s+:\S+/g' file
:first-var ||
|| :second-var

(com todo o :var || s impresso antes do || :var s).

Ou se você quiser apenas a parte :var , com o GNU grep com suporte a PCRE:

$ grep -Po ':\S+(?=\s+\|\|)|\|\|\s+\K:\S+' file
:first-var
:second-var

O mesmo que:

perl -lne 'print for /:\S+(?=\s+\|\|)|\|\|\s+\K:\S+/g'

Agora, se o que você deseja é extrair o :foo e o :bar em uma linha que sempre tem o formato anything :foo || :bar , você pode fazer isso de maneira padrão com:

s='[[:space:]]\{1,\}' S='[^[:space:]]\{1,\}'
sed -n "/\(:$S\}\)$s\{1,\}||$s\(:$S\).*/{
  s//\
\
/
  s/.*\n\(.*\n\)//p
}'
    
por 04.09.2017 / 17:34
1
perl -nE 'say "$1\n$2" if /(:\S+)\s*\|\|\s*(:\S+)/'
    
por 05.09.2017 / 16:23
0

Em vez de tentar criar uma expressão regular complicada, basta fazer duas passagens pelo arquivo:

grep -o '|| *[^ ]*' file
grep -o '[^ ]* *||' file

Ou combine com awk :

grep -o '[^ ]* *|| *[^ ]*' file | awk -F' *\|\| *' '{ print $1; print $2 }'

Dada a linha de exemplo

select * from table where :first-var || :second-var

que produziria

:first-var
:second-var
    
por 04.09.2017 / 19:25