Perbandingan huruf tidak sensitif pada string dalam skrip shell

129

The ==operator yang digunakan untuk membandingkan dua string di shell script. Namun, saya ingin membandingkan dua string mengabaikan case, bagaimana bisa dilakukan? Apakah ada perintah standar untuk ini?

Sachin Chourasiya
sumber

Jawaban:

72

jika Anda memiliki bash

str1="MATCH"
str2="match"
shopt -s nocasematch
case "$str1" in
 $str2 ) echo "match";;
 *) echo "no match";;
esac

jika tidak, Anda harus memberi tahu kami shell apa yang Anda gunakan.

alternatif, menggunakan awk

str1="MATCH"
str2="match"
awk -vs1="$str1" -vs2="$str2" 'BEGIN {
  if ( tolower(s1) == tolower(s2) ){
    print "match"
  }
}'
ghostdog74
sumber
32
Bagi siapa pun yang membandingkan string menggunakan ifpernyataan, shoptpendekatan mengharuskan Anda untuk menggunakan [[ ]]bentuk braket ganda kondisional daripada [ ]bentuk braket tunggal . Lihat juga: gnu.org/software/bash/manual/html_node/The-Shopt-Builtin.html
indiv
3
Pertanyaannya menunjukkan bahwa ==digunakan untuk membandingkan dua string, tetapi responnya menunjukkan perbandingan case-insensitive menggunakan casepernyataan. Meyakinkan, para shoptsolusi juga memungkinkan kasus-sensitif penggunaan ==, =~dan operator perbandingan string lainnya.
taranaki
7
Mungkin bijaksana untuk mengeksekusi shopt -u nocasematchsetelah perbandingan dilakukan untuk mengembalikan kembali ke default bash.
Ohad Schneider
6
Terbaik untuk menyimpan dan mengembalikan nocasematchpengaturan. Raih dengan SHELLNOCASEMATCH=`shopt -p nocasematch`lalu ubah dengan shopt -s nocasematchdan setelah selesai, kembalikan dengan$SHELLNOCASEMATCH
Urhixidur
2
Lebih baik lagi:, SHELLNOCASEMATCH=$(shopt -p nocasematch; true)karena shopt -pakan keluar dengan kode 1 jika opsi tidak disetel, dan ini dapat menyebabkan skrip dibatalkan jika set -esedang berlaku.
We Are All Monica
158

Di Bash, Anda dapat menggunakan ekspansi parameter untuk memodifikasi string ke semua huruf kecil / huruf besar:

var1=TesT
var2=tEst

echo ${var1,,} ${var2,,}
echo ${var1^^} ${var2^^}
alphaniner
sumber
14
Setidaknya balasan yang tidak menyiratkan opsi shopt. Jadi, Anda dapat membandingkan dua senar yang mengabaikan case dan dalam tes yang sama, bandingkan dua lainnya dengan kasing. Terima kasih
jehon
37
Apakah ini baru di Bash 4? Setidaknya dalam Bash 3.2.51 (digunakan dalam OS X 10.9) tidak berfungsi - echopernyataan pertama menghasilkan:-bash: ${var1,,}: bad substitution
Felix Rabe
1
Implementasi perbandingan case-insensitive seperti itu rentan terhadap masalah lokalisasi (seperti masalah Turki I). Saya tidak tahu bagaimana shopt -s nocasematchini diterapkan tetapi biasanya solusi "tingkat bahasa" seperti itu menanganinya dengan benar.
Ohad Schneider
5
@Ohad Schneider, biarkan orang Turki khawatir tentang masalah lokalisasi Turki, saya hanya perlu cara cepat dan efisien untuk mencocokkan string dan ini membutuhkan kue dengan margin huuuuuge
niken
1
pengguna macOS, tingkatkan versi Bash Anda. Anda sudah lebih dari satu dekade kedaluwarsa pada titik ini. itnext.io/upgrading-bash-on-macos-7138bd1066ba
Jason Carter
114

Semua jawaban ini mengabaikan cara termudah dan tercepat untuk melakukan ini (selama Anda memiliki Bash 4):

if [ "${var1,,}" = "${var2,,}" ]; then
  echo ":)"
fi

Yang Anda lakukan di sana adalah mengonversi kedua string menjadi huruf kecil dan membandingkan hasilnya.

Kerusuhan
sumber
6
Ini hanya tersedia di Bash 4 atau lebih baru (mis. Tidak pada Mac OS X 10.11)
d4Rk
8
@ d4Rk itulah sebabnya kalimat pertama dari jawaban saya mengatakan "selama Anda memiliki Bash 4". Karena itu, Bash 4 telah keluar selama lebih dari tujuh tahun pada saat menulis komentar ini, dan instalasi OS X saya sendiri telah memilikinya hampir selama itu.
Kerusuhan
@ Maaf, tidak memperhatikan hal itu sejak awal. Saya tahu Anda dapat menginstal bash apa pun yang Anda inginkan pada OS X, tetapi defaultnya adalah 3,2 dan karena skrip saya harus dijalankan pada Mac yang berbeda juga, ini bukan pilihan yang tepat bagi saya.
d4Rk
2
@ d4Rk itu bisa dimengerti - jika Anda benar-benar perlu menjamin portabilitas, saya sarankan tetap berpegang pada standar shell POSIX, karena Anda tidak dijamin menemukan versi bash sama sekali dalam beberapa kasus.
Kerusuhan
1
Ini persis apa yang saya cari. Harus lebih tinggi =)
Robin Winslow
39

Simpan status nocasematch (kalau-kalau ada fungsi lain yang tergantung padanya dinonaktifkan):

local orig_nocasematch=$(shopt -p nocasematch)
shopt -s nocasematch
[[ "foo" == "Foo" ]] && echo "match" || echo "notmatch"
$orig_nocasematch

Catatan: hanya gunakan localjika ada di dalam fungsi.

Gerry Hickman
sumber
4
Bagus karena casepernyataan (termasuk yang ada di jawaban ghostdog) akan selalu membuat kulit saya merangkak
SeldomNeedy
15

Salah satu caranya adalah dengan mengkonversi kedua string ke atas atau bawah:

test $(echo "string" | /bin/tr '[:upper:]' '[:lower:]') = $(echo "String" | /bin/tr '[:upper:]' '[:lower:]') && echo same || echo different

Cara lain adalah dengan menggunakan grep:

echo "string" | grep -qi '^String$' && echo same || echo different
Randy Proctor
sumber
Saya menggunakan trmetode ini di aplikasi Docker-ized saya berdasarkan alpine (yang menyediakan shvia busybox). Terima kasih.
MXWest
7

Untuk korn shell, saya menggunakan perintah bawaan typeset (-l untuk huruf kecil dan -u untuk huruf besar).

var=True
typeset -l var
if [[ $var == "true" ]]; then
    print "match"
fi
Ek C.
sumber
2
Ini jauh lebih baik, dalam hal kinerja daripada memulai awk atau proses lainnya.
Alex
1
Dalam bash, menyatakan -l atau -u dapat digunakan untuk mengatur atribut.
Bharat
5

Sangat mudah jika Anda ingin melakukan perbandingan case-insensitive line:

str1="MATCH"
str2="match"

if [[ $(fgrep -ix $str1 <<< $str2) ]]; then
    echo "case-insensitive match";
fi
Cooper F. Nelson
sumber
Akan lebih baik digunakan if fgrep -qix -- "$str1" <<<"$str2"; thensebagai gantinya.
3

Inilah solusi saya menggunakan tr:

var1=match
var2=MATCH
var1=`echo $var1 | tr '[A-Z]' '[a-z]'`
var2=`echo $var2 | tr '[A-Z]' '[a-z]'`
if [ "$var1" = "$var2" ] ; then
  echo "MATCH"
fi
batu333
sumber
3

grepmemiliki -iflag yang artinya case sensitive, jadi minta untuk memberi tahu Anda jika var2 ada di var1.

var1=match 
var2=MATCH 
if echo $var1 | grep -i "^${var2}$" > /dev/null ; then
    echo "MATCH"
fi
Larry
sumber
3
tidak akan berfungsi jika ada karakter khusus regex di var2.
haridsv
3

Untuk zshsintaks sedikit berbeda, tetapi masih lebih pendek daripada kebanyakan jawaban di sini:

> str1='mAtCh'
> str2='MaTcH'
> [[ "$str1:u" = "$str2:u" ]] && echo 'Strings Match!'
Strings Match!
>

Ini akan mengubah kedua string menjadi huruf besar sebelum perbandingan.


Metode lain menggunakan zsh's globbing flags, yang memungkinkan kita untuk secara langsung menggunakan pencocokan case-insensitive dengan menggunakan iflag glob:

setopt extendedglob
[[ $str1 = (#i)$str2 ]] && echo "Match success"
[[ $str1 = (#i)match ]] && echo "Match success"
smac89
sumber
1

shopt -s nocaseglob

ennuikiller
sumber
1

Saya menemukan blog / tutorial / hebat ini tentang berurusan dengan pola sensitif huruf . Tiga metode berikut dijelaskan secara rinci dengan contoh-contoh:

1. Ubah pola menjadi huruf kecil menggunakan perintah tr

opt=$( tr '[:upper:]' '[:lower:]' <<<"$1" )
case $opt in
        sql)
                echo "Running mysql backup using mysqldump tool..."
                ;;
        sync)
                echo "Running backup using rsync tool..."
                ;;
        tar)
                echo "Running tape backup using tar tool..."
                ;;
        *)
                echo "Other options"
                ;;
esac

2. Gunakan regex dengan pola kasus

opt=$1
case $opt in
        [Ss][Qq][Ll])
                echo "Running mysql backup using mysqldump tool..."
                ;;
        [Ss][Yy][Nn][Cc])
                echo "Running backup using rsync tool..."
                ;;
        [Tt][Aa][Rr])
                echo "Running tape backup using tar tool..."
                ;;
        *)
                echo "Other option"
                ;;
esac

3. Nyalakan nocasematch

opt=$1
shopt -s nocasematch
case $opt in
        sql)
                echo "Running mysql backup using mysqldump tool..."
                ;;
        sync)
                echo "Running backup using rsync tool..."
                ;;
        tar)
                echo "Running tape backup using tar tool..."
                ;;
        *)
                echo "Other option"
                ;;
esac

shopt -u nocasematch
Sherzad
sumber