Wyrażenie regularne w skrypcie bash

12

To jest mój pierwszy skrypt bashowy, więc prawdopodobnie popełniam łatwy błąd.

Zasadniczo próbuję napisać skrypt, który pobiera grupy użytkowników, a jeśli należą do określonej grupy, odpowiednio to zarejestruje. Oczywiście będzie więcej funkcji, ale nie ma sensu budować tego, kiedy nie mogę nawet uruchomić wyrażenia regularnego!

Do tej pory mam to:

#!/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

Każde miejsce, w którym próbowałem tego wyrażenia regularnego, działa. Ale w skrypcie bash zawsze wyświetla tylko $groups, a następnie No match. Czy ktoś może mi powiedzieć, co jest z tym nie tak?

jrdn
źródło
1
Co sprawia, że ​​myślisz, że coś jest z nią nie tak?
manatwork
1
@jrdnhannah, a następnie spróbuj powoli ponownie utworzyć docelowy regexp, najpierw dopasuj, ^([a-zA-Z0-9\-_]+)a następnie dodaj dwukropek i tak dalej ... powinieneś wkrótce dowiedzieć się, gdzie jest problem.
peterph
2
To samo tutaj z bash 4.2.45. Usunięcie podkreślenia naprawiło to. Dziwne. @jrdnhannah, czy mógłbyś napisać to jako odpowiedź i zaakceptować ją?
terdon
1
Ponieważ właśnie zarejestrowałem się w Unix SE, muszę czekać 8 godzin, zanim odpowiem na własne. Z przyjemnością zaznaczam to jako odpowiedź, jeśli zrobi to ktoś inny.
jrdn
4
@terdon bash prawdopodobnie wywołuje funkcje wyrażenia regularnego libc. To zależy od wersji libc, a nie wersji bash. Zobacz moją odpowiedź ... (a może nawet sekwencję zestawiania, której używasz)
derobert

Odpowiedzi:

13

Od man 7 regex:

Wyrażenie w nawiasie to lista znaków zawarta w „[]”. …

… Aby dodać dosłowne „-”, ustaw go jako pierwszy lub ostatni znak…. [A] Inne znaki specjalne, w tym „\”, tracą swoje specjalne znaczenie w wyrażeniu nawiasowym.

Próba wyrażenia regularnego z egrep daje błąd:

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

Oto prostsza wersja, która również daje błąd:

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

Ponieważ \nie jest wyjątkowy, jest to zakres, taki jak [a-z]byłby. Musisz umieścić swoje -na końcu, takie jak [_-]lub:

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

Powinno to działać niezależnie od wersji libc (w egrep lub bash).

edycja: Zależy to również od ustawień regionalnych. Strona ostrzega przed tym:

Zakresy są bardzo zależne od kolejności zestawiania, a programy przenośne powinny unikać na nich polegania.

Na przykład:

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

Oczywiście, mimo że nie popełnił błędu, nie robi tego, co chcesz:

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

Jest to zakres, który w ASCII, zawiera \, [, ^, i _.

derobert
źródło
Ciekawy. Mój egrepnie daje błędu, po prostu dopasowuje go poprawnie.
manatwork
@manatwork Twoja kolejność zestawiania prawdopodobnie pozwala na zasięg ...
derobert
Nie wiem wiele o sortowaniu. Masz na myśli to LC_COLLATE="en_US.UTF-8":?
manatwork
@manatwork Zredagowałem pytanie, aby podać przykład. Pamiętaj, że może być inaczej w twoim systemie, ponieważ czasami te sekwencje sortowania (sortowania) zmieniają się.
derobert
1
@manatwork W porządku, prawie złożyłem raport o błędzie, zanim zauważyłem próbę ucieczki -...
derobert
4

Ogólna reguła z wyrażeniami regularnymi (i wszelkimi błędami w większych fragmentach kodu): zmniejsz ją i odbuduj krok po kroku lub użyj dzielenia - cokolwiek będzie dla ciebie lepsze.

W tym przypadku sprawcą okazał się znak podkreślenia - ucieczka przed ukośnikiem sprawiła, że ​​zadziałał.

Peter
źródło