Advertisement
Advertisement


How to check if a string contains a substring in Bash


Question

I have a string in Bash:

string="My string"

How can I test if it contains another string?

if [ $string ?? 'foo' ]; then
  echo "It's there!"
fi

Where ?? is my unknown operator. Do I use echo and grep?

if echo "$string" | grep 'foo'; then
  echo "It's there!"
fi

That looks a bit clumsy.

2020/05/23
1
2673
5/23/2020 11:34:46 PM

Accepted Answer

You can use Marcus's answer (* wildcards) outside a case statement, too, if you use double brackets:

string='My long string'
if [[ $string == *"My long"* ]]; then
  echo "It's there!"
fi

Note that spaces in the needle string need to be placed between double quotes, and the * wildcards should be outside. Also note that a simple comparison operator is used (i.e. ==), not the regex operator =~.

2020/04/17
3795
4/17/2020 1:20:42 AM

If you prefer the regex approach:

string='My string';

if [[ $string =~ "My" ]]
then
   echo "It's there!"
fi
2019/05/20

I am not sure about using an if statement, but you can get a similar effect with a case statement:

case "$string" in 
  *foo*)
    # Do stuff
    ;;
esac
2008/10/23

stringContain variants (compatible or case independent)

As these Stack Overflow answers tell mostly about Bash, I've posted a case independent Bash function at the very bottom of this post...

Anyway, there is my

Compatible answer

As there are already a lot of answers using Bash-specific features, there is a way working under poorer-featured shells, like BusyBox:

[ -z "${string##*$reqsubstr*}" ]

In practice, this could give:

string='echo "My string"'
for reqsubstr in 'o "M' 'alt' 'str';do
  if [ -z "${string##*$reqsubstr*}" ] ;then
      echo "String '$string' contain substring: '$reqsubstr'."
    else
      echo "String '$string' don't contain substring: '$reqsubstr'."
    fi
  done

This was tested under Bash, Dash, KornShell (ksh) and ash (BusyBox), and the result is always:

String 'echo "My string"' contain substring: 'o "M'.
String 'echo "My string"' don't contain substring: 'alt'.
String 'echo "My string"' contain substring: 'str'.

Into one function

As asked by @EeroAaltonen here is a version of the same demo, tested under the same shells:

myfunc() {
    reqsubstr="$1"
    shift
    string="[email protected]"
    if [ -z "${string##*$reqsubstr*}" ] ;then
        echo "String '$string' contain substring: '$reqsubstr'.";
      else
        echo "String '$string' don't contain substring: '$reqsubstr'."
    fi
}

Then:

$ myfunc 'o "M' 'echo "My String"'
String 'echo "My String"' contain substring 'o "M'.

$ myfunc 'alt' 'echo "My String"'
String 'echo "My String"' don't contain substring 'alt'.

Notice: you have to escape or double enclose quotes and/or double quotes:

$ myfunc 'o "M' echo "My String"
String 'echo My String' don't contain substring: 'o "M'.

$ myfunc 'o "M' echo \"My String\"
String 'echo "My String"' contain substring: 'o "M'.

Simple function

This was tested under BusyBox, Dash, and, of course Bash:

stringContain() { [ -z "${2##*$1*}" ]; }

Then now:

$ if stringContain 'o "M3' 'echo "My String"';then echo yes;else echo no;fi
no
$ if stringContain 'o "M' 'echo "My String"';then echo yes;else echo no;fi
yes

... Or if the submitted string could be empty, as pointed out by @Sjlver, the function would become:

stringContain() { [ -z "${2##*$1*}" ] && [ -z "$1" -o -n "$2" ]; }

or as suggested by Adrian G√ľnter's comment, avoiding -o switches:

stringContain() { [ -z "${2##*$1*}" ] && { [ -z "$1" ] || [ -n "$2" ];};}

Final (simple) function:

And inverting the tests to make them potentially quicker:

stringContain() { [ -z "$1" ] || { [ -z "${2##*$1*}" ] && [ -n "$2" ];};}

With empty strings:

$ if stringContain '' ''; then echo yes; else echo no; fi
yes
$ if stringContain 'o "M' ''; then echo yes; else echo no; fi
no

Case independent (Bash only!)

For testing strings without care of case, simply convert each string to lower case:

stringContain() {
    local _lc=${2,,}
    [ -z "$1" ] || { [ -z "${_lc##*${1,,}*}" ] && [ -n "$2" ] ;} ;}

Check:

stringContain 'o "M3' 'echo "my string"' && echo yes || echo no
no
stringContain 'o "My' 'echo "my string"' && echo yes || echo no
yes
if stringContain '' ''; then echo yes; else echo no; fi
yes
if stringContain 'o "M' ''; then echo yes; else echo no; fi
no
2020/05/25

You should remember that shell scripting is less of a language and more of a collection of commands. Instinctively you think that this "language" requires you to follow an if with a [ or a [[. Both of those are just commands that return an exit status indicating success or failure (just like every other command). For that reason I'd use grep, and not the [ command.

Just do:

if grep -q foo <<<"$string"; then
    echo "It's there"
fi

Now that you are thinking of if as testing the exit status of the command that follows it (complete with semi-colon), why not reconsider the source of the string you are testing?

## Instead of this
filetype="$(file -b "$1")"
if grep -q "tar archive" <<<"$filetype"; then
#...

## Simply do this
if file -b "$1" | grep -q "tar archive"; then
#...

The -q option makes grep not output anything, as we only want the return code. <<< makes the shell expand the next word and use it as the input to the command, a one-line version of the << here document (I'm not sure whether this is standard or a Bashism).

2020/01/01

The accepted answer is best, but since there's more than one way to do it, here's another solution:

if [ "$string" != "${string/foo/}" ]; then
    echo "It's there!"
fi

${var/search/replace} is $var with the first instance of search replaced by replace, if it is found (it doesn't change $var). If you try to replace foo by nothing, and the string has changed, then obviously foo was found.

2013/06/12

Source: https://stackoverflow.com/questions/229551
Licensed under: CC-BY-SA with attribution
Not affiliated with: Stack Overflow
Email: [email protected]