Сравнение строк в сценарии оболочки без учета регистра


132

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

Ответы:


73

если у вас есть bash

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
индив,

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

7
Вероятно, разумно выполнить shopt -u nocasematchпосле того, как сравнение будет выполнено, чтобы вернуться к значениям по умолчанию для bash.
Охад Шнайдер

7
Лучше всего сохранить и восстановить nocasematchнастройку. Возьмите его с SHELLNOCASEMATCH=`shopt -p nocasematch`затем изменить его shopt -s nocasematchи когда - то сделали, восстановите его$SHELLNOCASEMATCH
Urhixidur

3
Еще лучше:, SHELLNOCASEMATCH=$(shopt -p nocasematch; true)потому что shopt -pзавершится с кодом 1, если параметр не установлен, и это может привести к прерыванию сценария, если set -eон действует.
We All Monica

160

В Bash вы можете использовать расширение параметра, чтобы преобразовать строку в нижний / верхний регистр:

var1=TesT
var2=tEst

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

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

38
Это новое в Bash 4? По крайней мере, в Bash 3.2.51 (используется в OS X 10.9) это не работает - первый echoоператор приводит к следующему:-bash: ${var1,,}: bad substitution
Felix Rabe

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

5
@ Охад Шнайдер, пусть турки беспокоятся о проблемах с турецкой локализацией, мне просто нужен быстрый и эффективный способ сопоставления строк, и это берет пирог с маржой huuuuuge
Никен

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

116

Все эти ответы игнорируют самый простой и быстрый способ сделать это (если у вас есть 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

@Riot, извините, я не заметил этого. Я знаю, что вы можете установить любой bash на OS X, но по умолчанию это 3.2, и поскольку мой сценарий также должен работать на разных компьютерах Mac, это не вариант для меня.
d4Rk

2
@ d4Rk это понятно - если вам действительно нужно гарантировать переносимость, я бы посоветовал придерживаться стандарта оболочки POSIX, поскольку в некоторых случаях вам не гарантировано найти какую-либо версию bash.
Riot

1
Это именно то, что я искал. Должен быть выше =)
Робин Уинслоу

40

Сохраните состояние nocasematch (если какая-то другая функция зависит от того, что она отключена):

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

Примечание: используйте только localесли он находится внутри функции.


4
Приятно, потому что caseзаявления (в том числе в ответе ghostdog) всегда вызывают
мурашки по

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метод в своих docker-ized приложениях на основе alpine (который предоставляет shчерез busybox). Спасибо.
MXWest

7

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

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

2
С точки зрения производительности это намного лучше, чем запуск awk или любого другого процесса.
Alex

1
В bash для установки атрибутов можно использовать declare -l или -u.
Bharat

6

Очень просто, если вы выполните fgrep для сравнения строк без учета регистра:

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флага glob:

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.