How to match case insensitive (uppercase and lower case) strings with regular expression in Bash

Written by - 0 comments

Published on - last updated on November 25th 2021 - Listed in Coding Bash Linux Shell


Bash is a great program language to quickly write scripts and tasks. But there is one particular thing Bash isn't well suited for: String comparisons (I'd use Perl for this). But with a little bit of Bash magic, this works well, too.

Basic string matching

There are string comparisons available using test. From the test man page:

        STRING1 = STRING2
              the strings are equal

       STRING1 != STRING2
              the strings are not equal

In general string comparisons work:

ck@mintp ~ $ var="linux"
ck@mintp ~ $ if [[ "$var" = "linux" ]]; then echo "yes"; else echo "no"; fi
yes

But what if one wants to know if a part of a string matches another string, kind of as a regular expression?

ck@mintp ~ $ var="linu"
ck@mintp ~ $ if [[ "$var" = "linux" ]]; then echo "yes"; else echo "no"; fi
no

Because the stored variable $var only contains a part of the string ("linu"), the string comparison does not work. test tries to compare both $var and the string and only if they match 100%, then the test would return true (or in this case "yes").

When running Bash in verbose mode (-x), the comparison can be seen:

ck@mintp ~ $ var="linu"
ck@mintp ~ $ set -x
set -x
ck@mintp ~ $ if [[ "$var" = "linux" ]]; then echo "yes"; else echo "no"; fi
+ [[ linu = \l\i\n\u\x ]]
+ echo no
no

The verbose mode clearly shows that [[ linu = \l\i\n\u\x ]] will not match (the backslashes can be ignored for the human eye).

Regular expression string matching

Bash however adds another feature to test: The regular expression hyphen (~). By using it in the test condition, Bash will do a regular expression matching.
But use caution! The position of the variable is highly important. If the variable $var is used on the left side, there will be no match:

ck@mintp ~ $ var="linu"
ck@mintp ~ $ if [[ "$var" =~ "linux" ]]; then echo "yes"; else echo "no"; fi
no

But if the variable is on the right side of the comparison, the regular match should work:

ck@mintp ~ $ var="linu"
ck@mintp ~ $ if [[ "linux" =~ "$var" ]]; then echo "yes"; else echo "no"; fi
yes

Important note: The regular expression hyphen (~) only works in Bash and in test conditions with double brackets [[ condition ]].

Now to the next problem: Case insensitive matching!

Case insensitive regular expression string matching

The previous tests always used lowercase letters and string matching worked. But what if the variable contains a mix of lowercase and uppercase letters?

ck@mintp ~ $ var="LinuX"
ck@mintp ~ $ if [[ "linux" =~ "$var" ]]; then echo "yes"; else echo "no"; fi
no

Because of the upperscale L and X in "LinuX", the variable doesn't match "linux" anymore - even with regular expression.

To handle this, Bash needs to be told to switch into "nocasematch" mode. This can be done using the shopt command (a Bash specific command to set or unset Shell options, see Shopt on Cyberciti):

ck@mintp ~ $ var="LinuX"
ck@mintp ~ $ shopt -s nocasematch; if [[ "linux" =~ "$var" ]]; then echo "yes"; else echo "no"; fi
yes

What about just a part of the full string?

ck@mintp ~ $ var="LiN"
ck@mintp ~ $ shopt -s nocasematch; if [[ "linux" =~ "$var" ]]; then echo "yes"; else echo "no"; fi
yes

The condition now finally returns "yes" because a case insensitive regular expression matches.

POSIX regular expression matching

But Bash can do even more than that; it can also use POSIX regular expressions - at least to a certain extend. Let's use above string comparison with multiple comparisons. The $var variable should be compared against multiple options:

ck@mintp ~ $ var="linux ubuntu"
ck@mintp ~ $ shopt -s nocasematch; if [[ ${var} =~ ^(linux|ubuntu|sles|debian|rhel) ]]; then echo "yes"; else echo "no"; fi
yes

The condition returns yes, as either "linux" and "ubuntu" strings were found.

If we use a different Operating System as string value for $var, it won't match:

ck@mintp ~ $ var="windows 2019"
ck@mintp ~ $ shopt -s nocasematch; if [[ ${var} =~ ^(linux|ubuntu|sles|debian|rhel) ]]; then echo "yes"; else echo "no"; fi
no



Add a comment

Show form to leave a comment

Comments (newest first)

No comments yet.

RSS feed

Blog Tags:

  AWS   Android   Ansible   Apache   Apple   Atlassian   BSD   Backup   Bash   Bluecoat   CMS   Chef   Cloud   Coding   Consul   Containers   CouchDB   DB   DNS   Database   Databases   Docker   ELK   Elasticsearch   Filebeat   FreeBSD   Galera   Git   GlusterFS   Grafana   Graphics   HAProxy   HTML   Hacks   Hardware   Icinga   Influx   Internet   Java   KVM   Kibana   Kodi   Kubernetes   LVM   LXC   Linux   Logstash   Mac   Macintosh   Mail   MariaDB   Minio   MongoDB   Monitoring   Multimedia   MySQL   NFS   Nagios   Network   Nginx   OSSEC   OTRS   Office   OpenSearch   PGSQL   PHP   Perl   Personal   PostgreSQL   Postgres   PowerDNS   Proxmox   Proxy   Python   Rancher   Rant   Redis   Roundcube   SSL   Samba   Seafile   Security   Shell   SmartOS   Solaris   Surveillance   Systemd   TLS   Tomcat   Ubuntu   Unix   VMWare   VMware   Varnish   Virtualization   Windows   Wireless   Wordpress   Wyse   ZFS   Zoneminder