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.

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

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



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.


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

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
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.