Expressão regular no script bash

13

Esta é a primeira vez que escrevo bash, então provavelmente estou cometendo um erro fácil.

Basicamente, estou tentando escrever um script que obtenha os grupos de um usuário e, se eles estiverem em um determinado grupo, registrará isso de acordo. Evidentemente, haverá mais funcionalidades, mas não adianta criar isso quando eu nem consigo fazer o regex funcionar!

Até agora, eu tenho o seguinte:

#!/bin/bash

regex="^([a-zA-Z0-9\-_]+ : [a-zA-Z0-9\-_]+) (usergroup)$"

# example output
groups="username : username usergroup"

echo "$groups" >> /home/jrdn/log

if [[ "$groups" =~ $regex ]]; then
    echo "Match!" >> /home/jrdn/log
else
    echo "No match" >> /home/jrdn/log
fi

Em todo lugar que eu tentei esse regex, ele funciona. Mas no script bash, ele apenas gera o $groups, seguido por No match. Então, alguém pode me dizer o que há de errado com isso?

jrdn
fonte
1
O que faz você pensar que algo está errado com isso?
manatwork
1
@jrdnhannah, em seguida, tente recriar lentamente o seu regexp de destino, primeiro faça a correspondência e ^([a-zA-Z0-9\-_]+)depois adicione os dois pontos e assim por diante ... você deve descobrir em breve, onde está o problema.
Peterph #
2
O mesmo aqui com o bash 4.2.45. Escapar do sublinhado o corrigiu. Esquisito. @jrdnhannah você poderia escrever isso como uma resposta e aceitá-la, por favor?
terdon
1
Como eu acabei de me inscrever no Unix SE, é preciso esperar 8 horas antes de responder o meu. Feliz em marcá-lo como respondido se alguém o fizer, no entanto.
JRDN
4
O @terdon bash chama as funções regex da libc, provavelmente. Portanto, depende da versão libc, não da versão bash. Veja a minha resposta ... (Ou talvez até mesmo na sequência de agrupamento que você tem em uso)
derobert

Respostas:

14

De man 7 regex:

Uma expressão entre colchetes é uma lista de caracteres entre "[]". ...

… Para incluir um literal '-', torne-o o primeiro ou o último caractere. [Todos] outros caracteres especiais, incluindo '\', perdem seu significado especial dentro de uma expressão entre colchetes.

Tentar o regexp com o egrep dá um erro:

$ echo "username : username usergroup" | egrep "^([a-zA-Z0-9\-_]+ : [a-zA-Z0-9\-_]+) (usergroup)$"
egrep: Invalid range end

Aqui está uma versão mais simples, que também fornece um erro:

$ echo 'hi' | egrep '[\-_]'
egrep: Invalid range end

Como \não é especial, é um intervalo, exatamente como [a-z]seria. Você precisa colocar o seu -no final, como [_-]ou:

echo "username : username usergroup" | egrep "^([a-zA-Z0-9_-]+ : [a-zA-Z0-9_-]+) (usergroup)$"
username : username usergroup

Isso deve funcionar independentemente da sua versão da libc (no egrep ou no bash).

editar: isso também depende das configurações de localidade. A página de manual alerta sobre isso:

Os intervalos são muito dependentes da sequência de intercalação e os programas portáteis devem evitar confiar neles.

Por exemplo:

$ echo '\_' | LC_ALL=en_US.UTF8 egrep '[\-_]'
egrep: Invalid range end
$ echo '\_' | LC_ALL=C egrep '[\-_]'
\_

Obviamente, mesmo que não tenha errado, não está fazendo o que você deseja:

$ echo '\^_' | LC_ALL=C egrep '^[\-_]+$'
\^_

É uma gama, que em ASCII, inclui \, [, ^, e _.

derobert
fonte
Interessante. Meu egrepnão dá erro, apenas corresponde corretamente.
manatwork
@manatwork sua sequência de agrupamento provavelmente permite que o intervalo ....
derobert
Eu não sei muito sobre agrupamento. Você quer dizer isso LC_COLLATE="en_US.UTF-8":?
manatwork
@manatwork Eu editei a pergunta para dar um exemplo. Observe que pode ser diferente no seu sistema, porque às vezes essas seqüências de agrupamento (classificação) são alteradas.
Derobert
1
@manatwork Sua OK, eu quase entrou com um relatório de bug antes de eu notei a tentativa de escapar -...
derobert
4

Regra geral com regexps (e quaisquer bugs em partes maiores de código): reduza-o e reconstrua-o passo a passo ou use a divisão - o que funcionar melhor para você.

Nesse caso, o culpado acabou sendo o sublinhado - escapar com uma barra invertida fez com que funcionasse.

peterph
fonte