Bagaimana saya bisa mencocokkan string dengan regex di Bash?

166

Saya mencoba untuk menulis script bash yang berisi fungsi sehingga ketika diberi .tar, .tar.bz2, .tar.gzdll file yang menggunakan tar dengan switch yang relevan untuk dekompresi file.

Saya menggunakan if elif lalu pernyataan yang menguji nama file untuk melihat apa yang berakhir dengan dan saya tidak bisa mencocokkan dengan menggunakan regach metacharacters.

Untuk menyimpan secara konstan penulisan ulang skrip yang saya gunakan 'test' di baris perintah, saya pikir pernyataan di bawah ini akan berfungsi, saya telah mencoba setiap kombinasi tanda kurung, kutipan, dan metacharat yang mungkin dan masih gagal.

test sed-4.2.2.tar.bz2 = tar\.bz2$; echo $?
(this returns 1, false)

Saya yakin masalahnya adalah yang sederhana dan saya telah mencari kemana-mana, namun saya tidak dapat mengerti bagaimana melakukannya. Apakah ada yang tahu bagaimana saya bisa melakukan ini?

pengguna1587462
sumber

Jawaban:

268

Untuk mencocokkan regex Anda perlu menggunakan =~operator.

Coba ini:

[[ sed-4.2.2.tar.bz2 =~ tar.bz2$ ]] && echo matched

Atau, Anda dapat menggunakan wildcard (bukan regex) dengan ==operator:

[[ sed-4.2.2.tar.bz2 == *tar.bz2 ]] && echo matched

Jika portabilitas tidak perhatian, saya sarankan menggunakan [[bukan [atau testkarena lebih aman dan lebih kuat. Lihat Apa perbedaan antara tes, [dan [[? untuk detail.

dogbane
sumber
7
Hati-hati dengan pencocokan wildcard glob pada contoh kedua. Di dalam [[]], * tidak diperluas seperti biasanya, untuk mencocokkan nama file di direktori saat ini yang cocok dengan pola. Contoh Anda berfungsi, tetapi sangat mudah untuk menggeneralisasi secara berlebihan dan secara keliru percaya bahwa * berarti mencocokkan apa pun di konteks apa pun. Ini hanya berfungsi seperti itu di dalam [[]]. Kalau tidak, itu akan diperluas ke nama file yang ada.
Alan Porter
7
Saya mencoba menggunakan tanda kutip di regex dan gagal; jawaban ini membantu membuat karya ini check="^a.*c$";if [[ "abc" =~ $check ]];then echo match;fikita perlu menyimpan regex pada var
Aquarius Power
Juga untuk dicatat bahwa regexp (seperti dalam perl) TIDAK boleh dalam tanda kurung: [[ sed-4.2.2.tar.bz2 == "*tar.bz2" ]]tidak akan berfungsi.
pevik
18
FWIW, sintaks untuk negasi (yaitu tidak cocok ) adalah [[ ! foo =~ bar ]].
Skippy le Grand Gourou
1
dash tidak mendukung -n 1parameter, juga tidak membuatnya secara otomatis ke dalam $REPLYvariabel. Awas!
54

Fungsi Untuk Melakukan Ini

extract () {
  if [ -f $1 ] ; then
      case $1 in
          *.tar.bz2)   tar xvjf $1    ;;
          *.tar.gz)    tar xvzf $1    ;;
          *.bz2)       bunzip2 $1     ;;
          *.rar)       rar x $1       ;;
          *.gz)        gunzip $1      ;;
          *.tar)       tar xvf $1     ;;
          *.tbz2)      tar xvjf $1    ;;
          *.tgz)       tar xvzf $1    ;;
          *.zip)       unzip $1       ;;
          *.Z)         uncompress $1  ;;
          *.7z)        7z x $1        ;;
          *)           echo "don't know '$1'..." ;;
      esac
  else
      echo "'$1' is not a valid file!"
  fi
}

Catatan lain

Menanggapi Aquarius Power dalam komentar di atas, We need to store the regex on a var

Variabel BASH_REMATCH diatur setelah Anda mencocokkan ekspresi, dan $ {BASH_REMATCH [n]} akan cocok dengan grup ke-n yang dibungkus dengan tanda kurung, yaitu sebagai berikut ${BASH_REMATCH[1]} = "compressed"dan${BASH_REMATCH[2]} = ".gz"

if [[ "compressed.gz" =~ ^(.*)(\.[a-z]{1,5})$ ]]; 
then 
  echo ${BASH_REMATCH[2]} ; 
else 
  echo "Not proper format"; 
fi

(Regex di atas tidak dimaksudkan untuk menjadi valid untuk penamaan file dan ekstensi, tetapi berfungsi sebagai contoh)

dualitas
sumber
juga perhatikan bahwa dengan tar BSD Anda dapat menggunakan "tar xf" untuk semua format dan tidak perlu perintah terpisah atau fungsi ini apa pun.
Orang Baik
apada GNU tar atau ppada BSD tar untuk secara eksplisit mengatakannya untuk secara otomatis menyimpulkan tipe kompresi dari ekstensi. GNU tar tidak akan melakukannya secara otomatis jika tidak, dan saya menduga dari komentar @GoodPerson bahwa tar BSD melakukannya secara default.
Mark K Cowan
7z dapat membuka paket .. AR, ARJ, CAB, CHM, CPIO, CramFS, DMG, EXT, FAT, GPT, HFS, IHEX, ISO, LZH, LZMA, MBR, MSI, NSIS, NTFS, QCOW2, RAR, RPM, SquashFS , UDF, UEFI, VDI, VHD, VMDK, WIM, XAR dan Z. lihat 7-zip.org
mosh
14

Saya tidak punya cukup perwakilan untuk berkomentar di sini, jadi saya mengirimkan jawaban baru untuk memperbaiki jawaban dogbane. Titik . di regexp

[[ sed-4.2.2.tar.bz2 =~ tar.bz2$ ]] && echo matched

akan benar-benar cocok dengan karakter apa pun, tidak hanya titik literal antara 'tar.bz2', misalnya

[[ sed-4.2.2.tar4bz2 =~ tar.bz2$ ]] && echo matched
[[ sed-4.2.2.tar§bz2 =~ tar.bz2$ ]] && echo matched

atau apa pun yang tidak mengharuskan melarikan diri dengan '\'. Sintaks yang ketat seharusnya

[[ sed-4.2.2.tar.bz2 =~ tar\.bz2$ ]] && echo matched

atau Anda bisa lebih cepat dan juga memasukkan titik sebelumnya di regex:

[[ sed-4.2.2.tar.bz2 =~ \.tar\.bz2$ ]] && echo matched
pengguna2066480
sumber
9

Karena Anda menggunakan bash, Anda tidak perlu membuat proses anak untuk melakukan ini. Berikut adalah salah satu solusi yang melakukannya sepenuhnya dalam bash:

[[ $TEST =~ ^(.*):\ +(.*)$ ]] && TEST=${BASH_REMATCH[1]}:${BASH_REMATCH[2]}

Penjelasan: Grup sebelum dan sesudah urutan "titik dua dan satu atau lebih spasi" disimpan oleh operator pencocokan pola dalam array BASH_REMATCH.

pengguna1934428
sumber
1
Perhatikan bahwa indeks 0 berisi kecocokan penuh dan indeks 1 dan 2 berisi kecocokan grup.
Rainer Schwarze
3
if [[ $STR == *pattern* ]]
then
    echo "It is the string!"
else
    echo "It's not him!"
fi

Bekerja untukku! GNU bash, version 4.3.11(1)-release (x86_64-pc-linux-gnu)

juan cortez
sumber
1
Ini sangat berbahaya; itu hanya berperilaku tanpa perilaku yang tidak ditentukan untuk Anda karena Anda tidak memiliki file dalam direktori saat ini bernama "pola" substring literal. Silakan, buat beberapa file dengan nama seperti itu, dan ekspansi substring akan cocok dengan file dan menghancurkan semuanya dengan heisenbug warna-warni.
i336_
Tetapi saya telah melakukan percobaan: dengan file `1pattern, pattern pattern2 dan pattern di direktori saat ini. Script ini berfungsi seperti yang diharapkan. Bisakah Anda memberi saya hasil tes Anda? @ i336_
juan
2
@ i336: Kurasa tidak. Di dalam [[ ... ]], pola rhs glob tidak berkembang sesuai dengan direktori saat ini, seperti biasanya.
user1934428
@ i336_ Tidak. Di dalam [[...]], Bash tidak melakukan ekspansi nama file. Dalam manual bash,Word splitting and filename expansion are not performed on the words between the [[ and ]];
jinbeom hong
@jinbeomhong: TIL. Itu bagus untuk diketahui, terima kasih!
i336_
2

shopt -s nocasematch

if [[ sed-4.2.2.$LINE =~ (yes|y)$ ]]
 then exit 0 
fi
Shyam Gupta
sumber