gunakan ekspresi reguler di if-condition di bash

88

Saya ingin tahu aturan umum untuk menggunakan ekspresi reguler di klausa if di bash?

Berikut ini contohnya

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

Mengapa tiga yang terakhir gagal cocok?

Semoga Anda bisa memberikan aturan umum sebanyak mungkin, tidak hanya untuk contoh ini.

Tim
sumber

Jawaban:

129

Saat menggunakan pola bola, tanda tanya mewakili satu karakter dan tanda bintang mewakili urutan nol atau lebih karakter:

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

Saat menggunakan ekspresi reguler, titik mewakili satu karakter dan tanda bintang mewakili nol atau lebih dari karakter sebelumnya. Jadi " .*" mewakili nol atau lebih karakter apa pun, " a*" mewakili nol atau lebih "a", " [0-9]*" mewakili nol atau lebih digit. Yang berguna lainnya (di antara banyak) adalah tanda tambah yang mewakili satu atau lebih karakter sebelumnya. Jadi " [a-z]+" mewakili satu atau lebih karakter alfa huruf kecil (di lokal C - dan beberapa lainnya).

if [[ $gg =~ ^....grid.*$ ]] ; then echo $gg; fi
Dennis Williamson
sumber
Jadi ada dua cara untuk mencocokkan string: pola glob dan ekspresi reguler? Apakah glob pettern tidak hanya digunakan untuk nama file? Di bash, kapan harus menggunakan pola glob dan kapan harus menggunakan ekspresi reguler? Terima kasih!
Tim
1
@Tim: Globbing tersedia di sebagian besar atau semua versi Bash. Pencocokan ekspresi reguler hanya tersedia di versi 3 dan lebih tinggi, tetapi saya akan merekomendasikan hanya menggunakannya di 3.2 dan yang lebih baru. Regex jauh lebih fleksibel daripada globbing.
Dennis Williamson
14
if [[ $gg =~ ^....grid.* ]]
Ignacio Vazquez-Abrams
sumber
1
Anda harus bisa menggunakan ". {4}" daripada "....", yaitu "^. {4} grid. *". Itu bisa lebih mudah dibaca dan dipahami.
pengguna276648
8

Menambahkan solusi ini dengan grepdan shbawaan dasar untuk mereka yang tertarik dengan solusi yang lebih portabel (terlepas dari bashversi; juga bekerja dengan yang lama sh, pada platform non-Linux dll.)

# 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    

Beberapa grepinkarnasi juga mendukung opsi -q(senyap) sebagai alternatif untuk pengalihan /dev/null, tetapi pengalihan sekali lagi adalah yang paling portabel.

vladr
sumber
lupa penutup ")" untuk egrep
ghostdog74
5
Gunakan grep -qsebagai ganti grep >/dev/null.
bfontaine
3

@OP,

Apakah glob pettern tidak hanya digunakan untuk nama file?

Tidak, pola "glob" tidak hanya digunakan untuk nama file. Anda juga dapat menggunakannya untuk membandingkan string. Dalam contoh Anda, Anda dapat menggunakan case / esac untuk mencari pola string.

 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

Di bash, kapan menggunakan pola glob dan kapan harus menggunakan ekspresi reguler? Terima kasih!

Regex lebih fleksibel dan "nyaman" daripada "pola glob", namun kecuali Anda melakukan tugas kompleks yang tidak dapat disediakan oleh "globbing / extended globbing", maka tidak perlu menggunakan regex. Regex tidak didukung untuk versi bash <3.2 (seperti yang disebutkan dennis), tetapi Anda masih dapat menggunakan extended globbing (dengan menyetel extglob). untuk globbing yang diperluas, lihat di sini dan beberapa contoh sederhana di sini .

Pembaruan untuk OP: Contoh untuk menemukan file yang dimulai dengan 2 karakter (titik "." Berarti 1 karakter) diikuti dengan "g" menggunakan regex

misalnya keluaran

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

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

Di atas, file dicocokkan karena namanya berisi 2 karakter diikuti oleh "g". (yaitu ..g).

Persamaannya dengan globbing akan menjadi seperti ini: (lihat referensi untuk arti ?dan *)

$ for file in ??g*; do echo $file; done
abg
degree
..g
anjing hantu74
sumber
Terima kasih ghostdog74. Di Bash dengan versi yang lebih tinggi dari 3.2, dapatkah ekspresi reguler digunakan untuk menggantikan pola glob di mana pun yang terakhir muncul? Atau ekspresi reguler hanya dapat digunakan dalam beberapa keadaan khusus? Misalnya, saya menemukan bahwa "ls ?? g" berfungsi sementara "ls ..g" tidak berfungsi.
Tim
Tidak ada yang bisa menghentikan Anda untuk menggunakan regex jika diperlukan. Terserah kamu. Catatan, sintaks regex berbeda dengan sintaks shell globbing. jadi ls ..gtidak bekerja. Anda memberi tahu shell untuk mencari file yang diberi nama ..g. Seperti untuk belajar tentang sintaks regex, Anda dapat mencoba perldoc perlretut, perldoc perlrequickatau melakukan info sedpada baris perintah.
ghostdog74