bash'da if-koşulunda düzenli ifade kullanın


88

Bash'de if cümlesinde normal ifadeyi kullanmak için genel kuralı merak ediyorum?

İşte bir örnek

$ 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  
$   

Son üç neden eşleşmiyor?

Umarım sadece bu örnek için değil, olabildiğince çok genel kural verebilirsiniz.

Yanıtlar:


129

Küre deseni kullanırken, soru işareti tek bir karakteri temsil eder ve yıldız işareti, sıfır veya daha fazla karakter dizisini temsil eder:

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

Normal bir ifade kullanırken, bir nokta tek bir karakteri temsil eder ve bir yıldız işareti önceki karakterin sıfır veya daha fazlasını temsil eder. Dolayısıyla " .*" herhangi bir karakterin a*sıfır veya daha fazlasını temsil eder , " " sıfır veya daha fazlasını temsil eder "a", " [0-9]*" sıfır veya daha fazla basamağı temsil eder. Diğer bir yararlı olan (birçokları arasında), önceki karakterlerden bir veya daha fazlasını temsil eden artı işaretidir. Yani " [a-z]+", bir veya daha fazla küçük harf alfa karakterini temsil eder (C yerel ayarında - ve bazılarında).

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

Öyleyse dizge eşleştirmenin iki yolu vardır: glob kalıbı ve normal ifade? Glob pettern sadece dosya isimleri için kullanılmıyor mu? Bash'de glob kalıbı ne zaman kullanılır ve normal ifade ne zaman kullanılır? Teşekkürler!
Tim

1
@Tim: Globbing, Bash'in çoğu veya tüm sürümlerinde mevcuttur. Normal ifade eşleşmesi yalnızca sürüm 3 ve daha yüksek sürümlerde mevcuttur, ancak bunu yalnızca 3.2 ve sonraki sürümlerde kullanmanızı öneririm. Normal ifadeler, küreselleşmeden çok daha çok yönlüdür.
Dennis Williamson



8

Daha taşınabilir bir çözümle ilgilenenler için bu çözümü grepve temel shyerleşikleri eklemek ( bashsürümden bağımsız ; shLinux olmayan platformlarda düz eski ile de çalışır vb.)

# 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    

Bazı grepenkarnasyonlar -q, yönlendirmeye alternatif olarak (sessiz) seçeneğini de destekler /dev/null, ancak yeniden yönlendirme yine en taşınabilir olanıdır.


egrep için ")" kapanışını unuttum
ghostdog74

5
grep -qBunun yerine kullanın grep >/dev/null.
bfontaine

3

@OP,

Glob pettern sadece dosya isimleri için kullanılmıyor mu?

Hayır, "glob" kalıbı yalnızca dosya adları için kullanılmaz. dizeleri karşılaştırmak için de kullanıyorsunuz. Örneklerinizde, dize modellerini aramak için case / esac kullanabilirsiniz.

 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

Bash'de glob kalıbı ne zaman kullanılır ve normal ifade ne zaman kullanılır? Teşekkürler!

Regex, "glob kalıpları" ndan daha çok yönlü ve "kullanışlıdır", ancak "globbing / extended globbing" in kolayca sağlayamayacağı karmaşık görevleri yapmıyorsanız, normal ifadeyi kullanmaya gerek yoktur. Normal ifade, bash <3.2 sürümü için desteklenmez (dennis'in belirtildiği gibi), ancak yine de genişletilmiş globbing'i (ayarlayarak extglob) kullanabilirsiniz. genişletilmiş Genellemenin için bkz burada ve bazı basit örnekler burada .

OP için güncelleme: 2 karakterle (noktalar "." 1 karakter anlamına gelir) ve ardından normal ifadeyi kullanarak "g" ile başlayan dosyaları bulma örneği

örneğin çıktı

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

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

Yukarıda, dosyalar, adları 2 karakter ve ardından "g" içerdiği için eşleştirilmiştir. (yani ..g).

(Bakmak: globbing ile eşdeğer böyle bir şey olacak referans ait anlam için ?ve *)

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

Teşekkürler ghostdog74. Bash 3.2'den daha yüksek sürümde, glob şablonunun göründüğü her yerde normal ifade yerine kullanılabilir mi? Veya normal ifade sadece bazı özel durumlarda kullanılabilir mi? Örneğin, "ls ?? g" nin çalıştığını, "ls ..g" nin çalışmadığını buldum.
Tim

Gerekirse normal ifadeyi kullanmak sizi durdurmaz. Sana kalmış. Normal ifade sözdiziminin kabuk genelleme sözdiziminden farklı olduğunu unutmayın. bu yüzden ls ..gçalışmıyor. Kabuğa adlandırılmış bir dosyayı aramasını söylüyorsunuz ..g. Regex sözdizimi hakkında öğrenme gelince, deneyebileceğiniz perldoc perlretut, perldoc perlrequickya da bir yapmak info sedkomut satırında.
ghostdog74
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.