205

The == operator is used to compare two strings in shell script. However, I want to compare two strings ignoring case, how can it be done? Is there any standard command for this?

Benjamin W.
  • 46,058
  • 19
  • 106
  • 116
Sachin
  • 20,805
  • 32
  • 86
  • 99
  • 4
    Quick note: No, `==` is _not_ the standard string comparison operator in shell; the only POSIX-standardized comparison operator is `=`. – Charles Duffy Oct 04 '21 at 18:43

14 Answers14

222

In Bash, you can use parameter expansion to modify a string to all lower-/upper-case:

var1=TesT
var2=tEst

echo ${var1,,} ${var2,,}
echo ${var1^^} ${var2^^}
alphaniner
  • 2,253
  • 2
  • 11
  • 2
  • 18
    At least a reply that does not imply the shopt option. So you can compare two string ignoring case and in the same test, compare two other with case. Thanks – jehon Mar 02 '14 at 06:30
  • 54
    Is this new in Bash 4? At least in Bash 3.2.51 (used in OS X 10.9) it does not work - the first `echo` statement results in: `-bash: ${var1,,}: bad substitution` – Felix Rabe Jun 11 '14 at 13:33
  • 1
    Such case-insensitive comparison implementations are prone to localization issues (such as the Turkish I problem). I don't know how `shopt -s nocasematch` is implemented but usually such "language-level" solutions handle it correctly. – Ohad Schneider Jan 12 '17 at 16:41
  • I'm with @FelixRabe; on macOS 10.13.6, which ships with Bash 3.2.57 and gives the same error – Ky - Feb 18 '19 at 16:25
  • ./anothertest.sh: line 4: ${var1,,}: bad substitution ./anothertest.sh: line 5: ${var1^^}: bad substitution – Starlton May 10 '19 at 21:44
  • 5
    both `,,` and `^^` work in `bash 4.4.20` but neither work in `zsh 5.4.2` (ubuntu 18.04) – Sang Oct 27 '19 at 15:10
179

All of these answers ignore the easiest and quickest way to do this (as long as you have Bash 4):

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

All you're doing there is converting both strings to lowercase and comparing the results.

Riot
  • 15,723
  • 4
  • 60
  • 67
  • 10
    This is only available in Bash 4 or newer (e.g. not on Mac OS X 10.11) – d4Rk May 11 '16 at 14:47
  • 15
    @d4Rk which is why the first sentence of my answer says "as long as you have Bash 4". Having said that, Bash 4 has been out for over seven years at the time of writing this comment, and my own OS X install has had it for almost that long. – Riot May 12 '16 at 00:24
  • @Riot sorry, didn't notice that in the first place. I know you can install whatever bash you want on OS X, but default is 3.2 and as my script must run on different Macs as well, this is not really an option for me. – d4Rk May 12 '16 at 06:38
  • 3
    @d4Rk that's understandable - if you really need to guarantee portability, I would suggest sticking to the POSIX shell standard, as you're not guaranteed to find any version of bash at all in some cases. – Riot May 12 '16 at 12:52
  • An additional comment to this cause it took me awhile to figure out. You HAVE to have a space inbetween the equality. – Michael Rountree May 11 '20 at 21:59
95

if you have bash

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

otherwise, you should tell us what shell you are using.

alternative, using awk

str1="MATCH"
str2="match"
awk -vs1="$str1" -vs2="$str2" 'BEGIN {
  if ( tolower(s1) == tolower(s2) ){
    print "match"
  }
}'
ghostdog74
  • 327,991
  • 56
  • 259
  • 343
  • 42
    For anyone comparing strings using `if` statements, the `shopt` approach requires you to use the double-bracket `[[ ]]` form of conditional instead of the single-bracket `[ ]` form. See also: http://www.gnu.org/software/bash/manual/html_node/The-Shopt-Builtin.html – indiv Sep 27 '12 at 22:37
  • 4
    The question indicates that `==` is used to compare two strings, but the response demonstrates case-insensitive comparison using a `case` statement. Reassuringly, the `shopt` solution also enables case-insensitive use of `==`, `=~`, and other string comparison operators. – taranaki Dec 01 '15 at 02:35
  • 13
    Probably wise to execute `shopt -u nocasematch` after the comparison is done in order to revert back to bash's default. – Ohad Schneider Jan 12 '17 at 16:44
  • 11
    Best to save and restore the `nocasematch` setting. Grab it with `SHELLNOCASEMATCH=\`shopt -p nocasematch\`` then change it with `shopt -s nocasematch` and once done, restore it with `$SHELLNOCASEMATCH` – Urhixidur Dec 05 '17 at 19:25
  • 7
    Better still: `SHELLNOCASEMATCH=$(shopt -p nocasematch; true)`, because `shopt -p` will exit with code 1 if the option is not set, and this can cause the script to abort if `set -e` is in effect. – We Are All Monica Aug 22 '18 at 03:51
  • To use awk's comparison in the bash script, I'd be tempted to do `if awk -vs1="$str1" -vs2="$str2" 'BEGIN {exit (tolower(s1)!=tolower(s2))}'; then echo "'$str1' and '$str2' match"; fi` instead. – mwfearnley Aug 15 '23 at 08:48
50

Save the state of nocasematch (in case some other function is depending on it being disabled):

local orig_nocasematch=$(shopt -p nocasematch; true)
shopt -s nocasematch

[[ "foo" == "Foo" ]] && echo "match" || echo "notmatch"

$orig_nocasematch

Note that local is only required inside a function.
Also, the ; true part will prevent the shell from exiting when set -e is on and $(shopt -p nocasematch) fails (because nocasematch was NOT set at all).

Top-Master
  • 7,611
  • 5
  • 39
  • 71
Gerry Hickman
  • 509
  • 4
  • 2
25

One way would be to convert both strings to upper or lower:

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

Another way would be to use grep:

echo "string" | grep -qi '^String$' && echo same || echo different
Randy Proctor
  • 7,396
  • 3
  • 25
  • 26
  • 1
    I used the `tr` method in my docker-ized applications based on alpine (which provides `sh` via `busybox`). Thank you. – MXWest Aug 30 '19 at 20:11
  • `test "$(...)" = "$(...)"` -- the double quotes are not optional for correct operation with all possible strings. – Charles Duffy Oct 04 '21 at 18:49
10

For zsh the syntax is slightly different, but still shorter than most answers here:

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

This will convert both strings to uppercase before the comparison.


Another method makes use zsh's globbing flags, which allows us to directly make use of case-insensitive matching by using the i glob flag:

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

# this example compares the variable with a literal string 'match'
[[ $str1 = (#i)match ]] && echo "Match success"
smac89
  • 39,374
  • 15
  • 132
  • 179
10

I came across this great blog/tutorial/whatever about dealing with case sensitive pattern. The following three methods are explained in details with examples:

1. Convert pattern to lowercase using tr command

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. Use careful globbing with case patterns

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. Turn on 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
bobbogo
  • 14,989
  • 3
  • 48
  • 57
Sherzad
  • 405
  • 4
  • 14
7

For korn shell, I use typeset built-in command (-l for lower-case and -u for upper-case).

var=True
typeset -l var
if [[ $var == "true" ]]; then
    print "match"
fi
Ek C.
  • 103
  • 1
  • 3
7

Very easy if you fgrep to do a case-insensitive line compare:

str1="MATCH"
str2="match"

if [[ $(fgrep -ix $str1 <<< $str2) ]]; then
    echo "case-insensitive match";
fi
jww
  • 97,681
  • 90
  • 411
  • 885
  • It would be better to use `if fgrep -qix -- "$str1" <<<"$str2"; then` instead. –  Nov 19 '19 at 23:07
4

grep has a -i flag which means case insensitive so ask it to tell you if var2 is in var1.

var1=match 
var2=MATCH 
if echo $var1 | grep -i "^${var2}$" > /dev/null ; then
    echo "MATCH"
fi
Eric Leschinski
  • 146,994
  • 96
  • 417
  • 335
Larry
  • 49
  • 1
4

Here is my solution using 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
stones333
  • 8,588
  • 1
  • 25
  • 27
  • `echo $var` needs quotes -- `echo "$var"`, or if `var1='hello * world'` you'll have a list of filenames in your `var1` after the operation even if it didn't contain such a list before. – Charles Duffy Oct 04 '21 at 18:48
0

shopt -s nocaseglob

ennuikiller
  • 46,381
  • 14
  • 112
  • 137
0

The 'tr' utility (translate characters) is always present on all Unix/Linux machines, and is light-weight.

Here is a function that can be used to address case insensitive comparisons. Since the exact location of 'tr' can vary, we first probe its likely locations and store the correct location in an env var appropriately named "BIN_TR".

declare BIN_TR=$( ls /bin/tr /usr/bin/tr 2>/dev/null | head -1 );

That is then used in the function declaration.

toLowerCase() {
  echo "$*" | $BIN_TR '[:upper:]' '[:lower:]'
}

A solution using 'tr' is expected to be highly portable between different variations of OS and OS set-up. While 'awk' is also highly probable, the 'tr' utility is tiny compared to 'awk', and so a function using 'tr' is presumably lighter weight.

IAM_AL_X
  • 1,221
  • 11
  • 12
  • Why would you use `... | $BIN_TR` this way instead of just `... | tr`? The shell itself automatically does a PATH lookup _and caches that lookup_ -- and its built-in lookup is a lot more efficient than a hack using `ls`. – Charles Duffy Oct 04 '21 at 18:44
  • Moreover, some distros don't use `/bin` or `/usr/bin` at all. I'm writing this on a NixOS machine; my copy of `tr` comes from `/run/current-system/sw/bin/tr`, which is a symlink to `/nix/store/cc4nnlspm4pwmp5rvjl6wpy9nyzcsbnr-coreutils-8.31/bin/tr`. – Charles Duffy Oct 04 '21 at 18:45
-2

On Unix-like operating systems, the test command checks file types and compares values.

str1="MATCH"
str2="match"

if test $str1 =  $str2
  then
  echo "equal yes"
else
  echo "equal not"
fi

On Unix-like operating systems, the test command checks file types and compares values.

it's very simple that way.

in a few lines of code

cardosource
  • 165
  • 1
  • 7