Porównanie ciągów znaków w skrypcie powłoki bez uwzględniania wielkości liter

139

==Operator służy do porównywania dwóch ciągów w skorupkach skryptu. Jednak chcę porównać dwa ciągi, ignorując wielkość liter, jak to zrobić? Czy jest do tego jakieś standardowe polecenie?

Sachin Chourasiya
źródło

Odpowiedzi:

77

jeśli masz bash

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

w przeciwnym razie powiedz nam, jakiej powłoki używasz.

alternatywa, używając awk

str1="MATCH"
str2="match"
awk -vs1="$str1" -vs2="$str2" 'BEGIN {
  if ( tolower(s1) == tolower(s2) ){
    print "match"
  }
}'
ghostdog74
źródło
33
Dla każdego, kto porównuje ciągi przy użyciu ifinstrukcji, shoptpodejście to wymaga użycia [[ ]]formy warunkowej z podwójnym nawiasem zamiast postaci z pojedynczym nawiasem [ ]. Zobacz też: gnu.org/software/bash/manual/html_node/The-Shopt-Builtin.html
indyw.
4
Pytanie wskazuje, że ==jest używane do porównywania dwóch ciągów, ale odpowiedź demonstruje porównanie bez rozróżniania wielkości liter przy użyciu caseinstrukcji. Uspokajająco The shoptrozwiązanie umożliwia również bez uwzględniania wielkości liter wykorzystania ==, =~oraz innych operatorów porównania ciąg.
taranaki
9
Prawdopodobnie rozsądne wykonanie shopt -u nocasematchpo wykonaniu porównania, aby powrócić do domyślnych ustawień basha.
Ohad Schneider,
8
Najlepiej zapisać i przywrócić nocasematchustawienie. Chwyć go, a SHELLNOCASEMATCH=`shopt -p nocasematch`następnie zmień za pomocą, shopt -s nocasematcha po $SHELLNOCASEMATCH
zakończeniu
4
Jeszcze lepiej: SHELLNOCASEMATCH=$(shopt -p nocasematch; true)ponieważ shopt -pzakończy pracę z kodem 1, jeśli opcja nie jest ustawiona, a to może spowodować przerwanie działania skryptu, jeśli set -ejest aktywny.
Wszyscy jesteśmy Monica
166

W Bash możesz użyć interpretacji parametrów, aby zmodyfikować ciąg znaków na małe / duże litery:

var1=TesT
var2=tEst

echo ${var1,,} ${var2,,}
echo ${var1^^} ${var2^^}
alfaniner
źródło
14
Przynajmniej odpowiedź, która nie sugeruje opcji shopt. Możesz więc porównać dwa ciągi ignorujące wielkość liter i w tym samym teście porównać dwa inne z wielkością liter. Dzięki
jehon
40
Czy to nowość w Bash 4? Przynajmniej w Bash 3.2.51 (używany w OS X 10.9) nie działa - pierwsze echostwierdzenie skutkuje:-bash: ${var1,,}: bad substitution
Felix Rabe
1
Takie implementacje porównań bez rozróżniania wielkości liter są podatne na problemy z lokalizacją (takie jak turecki problem I). Nie wiem, jak shopt -s nocasematchjest zaimplementowany, ale zazwyczaj takie rozwiązania "językowe" radzą sobie z tym poprawnie.
Ohad Schneider,
7
@Ohad Schneider, niech Turcy martwią się tureckimi problemami z lokalizacją, potrzebuję tylko szybkiego i wydajnego sposobu na dopasowanie ciągu, a to zajmuje ciasto przez huuuuuge margines
niken
2
oba ,,i ^^pracują, bash 4.4.20ale żadne nie pracują zsh 5.4.2(ubuntu 18.04)
transang
126

Wszystkie te odpowiedzi ignorują najłatwiejszy i najszybszy sposób na zrobienie tego (o ile masz Bash 4):

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

Wszystko, co tam robisz, to konwertowanie obu ciągów na małe litery i porównywanie wyników.

Zamieszki
źródło
6
Jest to dostępne tylko w Bash 4 lub nowszym (np. Nie w Mac OS X 10.11)
d4Rk
8
@ d4Rk, dlatego w pierwszym zdaniu mojej odpowiedzi jest napisane „tak długo, jak masz Bash 4”. Powiedziawszy to, Bash 4 był niedostępny przez ponad siedem lat w momencie pisania tego komentarza, a moja własna instalacja OS X miała go prawie tak długo.
Zamieszki
@Riot przepraszam, nie zauważyłem tego w pierwszej kolejności. Wiem, że możesz zainstalować dowolne bash na OS X, ale domyślnie jest to 3.2, a ponieważ mój skrypt musi działać również na różnych komputerach Mac, nie jest to dla mnie opcja.
d4Rk
2
@ d4Rk to zrozumiałe - jeśli naprawdę potrzebujesz zagwarantować przenośność, sugerowałbym trzymać się standardu powłoki POSIX, ponieważ w niektórych przypadkach nie masz gwarancji, że w ogóle znajdziesz jakąkolwiek wersję basha.
Zamieszki
1
To jest dokładnie to, czego szukałem. Powinien być wyższy =)
Robin Winslow
41

Zapisz stan nocasematch (w przypadku, gdy inna funkcja zależy od jej wyłączenia):

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

Uwaga: używaj tylko localwtedy, gdy znajduje się wewnątrz funkcji.

Gerry Hickman
źródło
4
Fajnie, ponieważ casestwierdzenia (w tym te w odpowiedzi Ghostdog) zawsze sprawią, że moja skóra zacznie pełzać
Rzadko
15

Jednym ze sposobów byłoby przekonwertowanie obu ciągów na wyższy lub niższy:

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

Innym sposobem byłoby użycie grep:

echo "string" | grep -qi '^String$' && echo same || echo different
Randy Proctor
źródło
Użyłem tej trmetody w moich dokerizowanych aplikacjach opartych na alpine (który dostarcza shvia busybox). Dziękuję Ci.
MXWest
7

W przypadku powłoki Korn używam wbudowanego polecenia typeset (-l dla małych liter i -u dla dużych).

var=True
typeset -l var
if [[ $var == "true" ]]; then
    print "match"
fi
Ek C.
źródło
2
Jest to o wiele lepsze pod względem wydajności niż uruchamianie awk czy jakiegokolwiek innego procesu.
Alex
1
W bash do ustawienia atrybutów można użyć deklaracji -l lub -u.
Bharat
6

Bardzo proste, jeśli fgrep zrobisz porównanie linii bez rozróżniania wielkości liter:

str1="MATCH"
str2="match"

if [[ $(fgrep -ix $str1 <<< $str2) ]]; then
    echo "case-insensitive match";
fi
Cooper F. Nelson
źródło
Lepiej byłoby użyć if fgrep -qix -- "$str1" <<<"$str2"; thenzamiast tego.
3

Oto moje rozwiązanie wykorzystujące 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
kamienie333
źródło
3

grepma -iflagę, która oznacza, że ​​wielkość liter nie jest rozróżniana, więc poproś o informację, czy zmienna2 jest w zmiennej1.

var1=match 
var2=MATCH 
if echo $var1 | grep -i "^${var2}$" > /dev/null ; then
    echo "MATCH"
fi
Larry
źródło
3
nie zadziała, jeśli w zmiennej var2 są jakieś znaki specjalne wyrażenia regularnego.
haridsv
3

Ponieważ zshskładnia jest nieco inna, ale nadal krótsza niż większość odpowiedzi tutaj:

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

Spowoduje to konwersję obu ciągów na wielkie litery przed porównaniem.


Inna metoda wykorzystuje zsh globbing flags, co pozwala nam bezpośrednio korzystać z dopasowywania bez uwzględniania wielkości liter za pomocą iflagi glob:

setopt extendedglob
[[ $str1 = (#i)$str2 ]] && echo "Match success"
[[ $str1 = (#i)match ]] && echo "Match success"
smac89
źródło
2

Natknąłem się na ten świetny blog / samouczek / cokolwiek o radzeniu sobie z wzorcem uwzględniającym wielkość liter . Poniższe trzy metody są szczegółowo wyjaśnione z przykładami:

1. Przekonwertuj wzór na małe litery za pomocą polecenia 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. Używaj ostrożnego globowania z wzorami wielkości liter

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. Włącz 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
źródło
1

shopt -s nocaseglob

ennuikiller
źródło