Порівняння рядків рядків у сценарії оболонки


129

==Оператор використовується для порівняння двох рядків в скрипті оболонки. Однак я хочу порівняти два рядки ігнорування випадку, як це можна зробити? Чи є для цього стандартна команда?

Відповіді:


72

якщо у вас баш

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

в іншому випадку ви повинні сказати нам, яку оболонку ви використовуєте.

альтернативно, використовуючи awk

str1="MATCH"
str2="match"
awk -vs1="$str1" -vs2="$str2" 'BEGIN {
  if ( tolower(s1) == tolower(s2) ){
    print "match"
  }
}'

32
Для тих, хто порівнює рядки, використовуючи ifоператори, shoptпідхід вимагає використовувати [[ ]]умовну форму з подвійною дужкою замість форми з однією дужкою [ ]. Дивіться також: gnu.org/software/bash/manual/html_node/The-Shopt-Builtin.html
Indiv

3
Питання вказує, що ==використовується для порівняння двох рядків, але відповідь демонструє порівняння з урахуванням регістру, використовуючи caseоператор. Обнадіює, то shoptрішення також дозволяє регістронезавісімого використання ==, =~і інші оператори порівняння рядків.
таранакі

7
Можливо, розумно виконати shopt -u nocasematchпісля порівняння, щоб повернути назад до дефолту bash.
Охад Шнайдер

6
Найкраще зберегти та відновити nocasematchналаштування. Візьміть його, SHELLNOCASEMATCH=`shopt -p nocasematch`потім змініть його shopt -s nocasematchі, як тільки буде зроблено, відновіть його$SHELLNOCASEMATCH
Urhixidur

2
Ще краще: SHELLNOCASEMATCH=$(shopt -p nocasematch; true)тому, що shopt -pвийде з кодом 1, якщо параметр не встановлений, і це може спричинити скасування сценарію, якщо set -eвін діє.
Ми всі Моніка

158

У Bash ви можете використовувати параметри розширення, щоб змінити рядок для всього нижнього / верхнього регістру:

var1=TesT
var2=tEst

echo ${var1,,} ${var2,,}
echo ${var1^^} ${var2^^}

14
Принаймні відповідь, яка не передбачає зніманого варіанту. Таким чином, ви можете порівняти два рядки ігнорування рядків і в одному і тому ж тесті, порівняти два інші з регістром. Спасибі
jehon

37
Це нове в Bash 4? Принаймні, у Bash 3.2.51 (використовується в OS X 10.9) це не працює - перше echoтвердження призводить до:-bash: ${var1,,}: bad substitution
Фелікс Рабе

1
Такі реальні порівняльні реалізації схильні до проблем локалізації (наприклад, проблема турецької I). Я не знаю, як shopt -s nocasematchце реалізовано, але зазвичай такі рішення на рівні "мови" справляються з цим правильно.
Охад Шнайдер

5
@Ohad Schneider, нехай турки турбуються про проблеми турецької локалізації, мені просто потрібен швидкий та ефективний спосіб поєднати рядок, і це займає пиріг за великим запасом
поєднати

1
Користувачі macOS, оновіть свою версію Bash. З цього моменту застаріло більше десятиліття. itnext.io/upgrading-bash-on-macos-7138bd1066ba
Джейсон Картер

114

Усі ці відповіді ігнорують найпростіший і швидкий спосіб зробити це (поки у вас є Bash 4):

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

Все, що ви там робите, - це перетворення обох рядків у малі та порівняння результатів.


6
Це доступно лише в Bash 4 або новіших версіях (наприклад, не на Mac OS X 10.11)
d4Rk

8
@ d4Rk, тому перше речення моєї відповіді говорить "поки у вас є Bash 4". Сказавши це, на момент написання цього коментаря Bash 4 вже понад сім років, і моя власна установка OS X мала його майже так довго.
Бунт

@Riot вибачте, в першу чергу цього не помітили. Я знаю, що ви можете встановити будь-який bash, який ви хочете, на OS X, але за замовчуванням 3,2, і оскільки мій сценарій також повинен працювати на різних Macs, це для мене насправді не варіант.
d4Rk

2
@ d4Rk, що зрозуміло - якщо вам справді потрібно гарантувати портативність, я б запропонував дотримуватися стандарту оболонки POSIX, оскільки ви не гарантуєтесь, що взагалі знайдете будь-яку версію bash.
Бунт

1
Це саме те, що я шукав. Повинен бути вище =)
Робін Вінслов

39

Збережіть стан nocasematch (у випадку, якщо якась інша функція залежить від її відключення):

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

Примітка. Використовуйте лише, localякщо він знаходиться у межах функції.


4
Приємно, тому що caseзаяви (включаючи відповіді у відповіді
привидця

15

Одним із способів було б перетворення обох рядків у верхній або нижній:

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

Іншим способом було б використання grep:

echo "string" | grep -qi '^String$' && echo same || echo different

Я використовував цей trметод у своїх докерських програмах на основі альпійських (що надає shчерез busybox). Дякую.
MXWest

7

Для оболонки korn я використовую вбудовану команду typeset (-l для нижнього регістру та -u для верхнього регістру).

var=True
typeset -l var
if [[ $var == "true" ]]; then
    print "match"
fi

2
Це набагато краще, з точки зору продуктивності, ніж запуск awk або будь-який інший процес.
Олексій

1
У bash, для встановлення атрибутів можна використовувати -l або -u.
Бхарат

5

Дуже просто, якщо ви перекочуєтесь, щоб порівняти нечутливу до регістру лінію:

str1="MATCH"
str2="match"

if [[ $(fgrep -ix $str1 <<< $str2) ]]; then
    echo "case-insensitive match";
fi

Краще використовувати if fgrep -qix -- "$str1" <<<"$str2"; thenзамість цього.

3

Ось моє рішення з використанням 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

3

grepмає -iпрапор, що означає нечутливий регістр, тому попросіть його повідомити, чи є var2 у var1.

var1=match 
var2=MATCH 
if echo $var1 | grep -i "^${var2}$" > /dev/null ; then
    echo "MATCH"
fi

3
не працюватиме, якщо в var2 є спеціальні символи регулярного вираження.
haridsv

3

Для zshсинтаксису трохи відрізняється, але все ж менше , ніж більшість відповідей тут:

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

Це перетворить обидва рядки у великі регістри перед порівнянням.


Інший метод використовує zsh globbing flags, який дозволяє нам безпосередньо використовувати збіг, що не враховує регістри, використовуючи iпрапор глобуса:

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


1

Я натрапив на цей чудовий блог / навчальний посібник / що завгодно щодо роботи з чутливою до регістру моделлю . Наступні три методи детально пояснюються прикладами:

1. Перетворення шаблону в малі регістри за допомогою команди 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. Використовуйте регулярний вираз із шаблонами

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. Увімкніть 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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.