użyj wyrażenia regularnego w warunku-if w bash


88

Zastanawiam się, jaka jest ogólna zasada używania wyrażenia regularnego w klauzuli if w bash?

Oto przykład

$ gg=svm-grid-ch  
$ if [[ $gg == *grid* ]] ; then echo $gg; fi  
svm-grid-ch  
$ if [[ $gg == ^....grid* ]] ; then echo $gg; fi  
$ if [[ $gg == ....grid* ]] ; then echo $gg; fi  
$ if [[ $gg == s...grid* ]] ; then echo $gg; fi  
$   

Dlaczego ostatnie trzy nie pasują?

Mam nadzieję, że możesz podać jak najwięcej ogólnych zasad, nie tylko na potrzeby tego przykładu.

Odpowiedzi:


129

Gdy używasz wzorca glob, znak zapytania reprezentuje pojedynczy znak, a gwiazdka reprezentuje sekwencję zera lub więcej znaków:

if [[ $gg == ????grid* ]] ; then echo $gg; fi

Gdy używasz wyrażenia regularnego, kropka reprezentuje pojedynczy znak, a gwiazdka reprezentuje zero lub więcej poprzedzającego znaku. Zatem „ .*” oznacza zero lub więcej dowolnego znaku, „ a*” oznacza zero lub więcej „a”, „ [0-9]*” oznacza zero lub więcej cyfr. Kolejnym użytecznym (spośród wielu) jest znak plus, który reprezentuje jeden lub więcej poprzedzających znaków. Tak więc „ [a-z]+” reprezentuje jedną lub więcej małych liter alfa (w języku C - i kilka innych).

if [[ $gg =~ ^....grid.*$ ]] ; then echo $gg; fi

Więc są dwa sposoby dopasowania ciągów: wzorzec glob i wyrażenie regularne? Czy glob pettern jest używany nie tylko w nazwach plików? Kiedy w bashu używać wzorca glob, a kiedy wyrażenia regularnego? Dzięki!
Tim

1
@Tim: Globbing jest dostępny w większości lub we wszystkich wersjach Bash. Dopasowywanie regex jest dostępne tylko w wersji 3 i nowszych, ale polecam używanie go tylko w wersji 3.2 i nowszych. Regexy są znacznie bardziej wszechstronne niż globbing.
Wstrzymano do odwołania.



8

Dodanie tego rozwiązania z greppodstawowymi shwbudowanymi wersjami dla osób zainteresowanych bardziej przenośnym rozwiązaniem (niezależnie od bashwersji; działa również ze zwykłym starym sh, na platformach innych niż Linux itp.)

# GLOB matching
gg=svm-grid-ch    
case "$gg" in
   *grid*) echo $gg ;;
esac

# REGEXP    
if echo "$gg" | grep '^....grid*' >/dev/null ; then echo $gg ; fi    
if echo "$gg" | grep '....grid*' >/dev/null ; then echo $gg ; fi    
if echo "$gg" | grep 's...grid*' >/dev/null ; then echo $gg ; fi    

# Extended REGEXP
if echo "$gg" | egrep '(^....grid*|....grid*|s...grid*)' >/dev/null ; then
  echo $gg
fi    

Niektóre grepinkarnacje obsługują również -qopcję (cichą) jako alternatywę dla przekierowania do /dev/null, ale przekierowanie jest ponownie najbardziej przenośne.


zapomniałem zamykającego „)” dla egrep
ghostdog74

5
Użyj grep -qzamiast grep >/dev/null.
bfontaine

3

@OP,

Czy glob pettern jest używany nie tylko w nazwach plików?

Nie, wzorzec „glob” jest używany nie tylko w nazwach plików. używasz go również do porównywania łańcuchów. W swoich przykładach możesz użyć przypadku / esac, aby wyszukać wzorce łańcuchów.

 gg=svm-grid-ch 
 # looking for the word "grid" in the string $gg
 case "$gg" in
    *grid* ) echo "found";;
 esac

 # [[ $gg =~ ^....grid* ]]
 case "$gg" in ????grid*) echo "found";; esac 

 # [[ $gg =~ s...grid* ]]
 case "$gg" in s???grid*) echo "found";; esac

Kiedy w bashu używać wzorca glob, a kiedy wyrażenia regularnego? Dzięki!

Regex są bardziej wszechstronne i „wygodne” niż „wzorce globalne”, jednak jeśli nie wykonujesz złożonych zadań, których „globalizacja / rozszerzone globowanie” nie może łatwo zapewnić, nie ma potrzeby używania wyrażenia regularnego. Regex nie jest obsługiwany w wersji bash <3.2 (jak wspomniał Dennis), ale nadal możesz używać rozszerzonego globowania (poprzez ustawienie extglob). rozszerzony globbing, zobacz tutaj i kilka prostych przykładów tutaj .

Aktualizacja dla OP: przykład wyszukiwania plików, które zaczynają się od 2 znaków (kropki „.” Oznaczają 1 znak), po których następuje „g” przy użyciu wyrażenia regularnego

np. wyjście

$ shopt -s dotglob
$ ls -1 *
abg
degree
..g

$ for file in *; do [[ $file =~ "..g" ]] && echo $file ; done
abg
degree
..g

W powyższym przypadku pliki są dopasowywane, ponieważ ich nazwy zawierają 2 znaki, po których następuje „g”. (tj ..g.).

Odpowiednik z globbingiem będzie wyglądał mniej więcej tak: (spójrz na odniesienie dla znaczenia ?i *)

$ for file in ??g*; do echo $file; done
abg
degree
..g

Dzięki ghostdog74. Czy w Bash z wersją wyższą niż 3.2 można użyć wyrażenia regularnego do zastąpienia wzorca glob, gdziekolwiek pojawia się ten drugi? Czy może wyrażenie regularne może być używane tylko w szczególnych okolicznościach? Na przykład stwierdziłem, że „ls ?? g” działa, a „ls ..g” nie.
Tim

Nic nie stoi na przeszkodzie, abyś używał wyrażenia regularnego, jeśli jest taka potrzeba. To zależy od Ciebie. Uwaga, składnia wyrażeń regularnych różni się od składni globbowania powłoki. więc ls ..gnie działa. Mówisz powłoce, aby szukała pliku o nazwie ..g. Co do poznawania składni regex, można spróbować perldoc perlretut, perldoc perlrequickalbo zrobić info sedz linii poleceń.
ghostdog74
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.