not - bash string substring

How to check if a string contains a substring in Bash (14)

Compatible answer

As there is 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'."
      echo "String '$string' don't contain substring: '$reqsubstr'."

This was tested under bash, dash, 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 same demo, tested under the same shells:

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


$ 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*}" ]; }

That's all folks!

Then now:

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

... Or if the submitted string could be empty, as pointed 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 switche:

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

With empty strings:

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

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!"

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

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

That looks a bit clumsy.

grep -q is useful for this purpose.

The same using awk:

string="unix-bash 2389"
printf '%s' "$string" | awk -vc="$character" '{ if (gsub(c, "")) { print "Found" } else { print "Not Found" } }'


Not Found

string="unix-bash 2389"
printf '%s' "$string" | awk -vc="$character" '{ if (gsub(c, "")) { print "Found" } else { print "Not Found" } }'



Original source:

Bash4+ examples. Note: not using quotes will cause issues when words contain spaces etc.. Always quote in bash IMO.

Here are some examples BASH4+ :

Example 1, check for 'yes' in string (case insensitive):

    if [[ "${str,,}" == *"yes"* ]] ;then

Example 2, check for 'yes' in string (case insensitive):

    if [[ "$(echo "$str" | tr '[:upper:]' '[:lower:]')" == *"yes"* ]] ;then

Example 3, check for 'yes' in string (case sensitive) :

     if [[ "${str}" == *"yes"* ]] ;then

Example 4, check for 'yes' in string (case sensitive):

     if [[ "${str}" =~ "yes" ]] ;then

Example 5, exact match (case sensitive):

     if [[ "${str}" == "yes" ]] ;then

Example 6, exact match (case insensitive):

     if [[ "${str,,}" == "yes" ]] ;then

Example 7, exact match :

     if [ "$a" = "$b" ] ;then


Exact word match:

string='My long string'

if grep -E -q "\b${exactSearch}\b" <<<${string} >/dev/null 2>&1
    echo "It's there"

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

case "$string" in 
    # Do stuff

I found to need this functionality quite frequently, so I'm using a home-made shell function in my .bashrc like this which allows me to re-use it as often as I need to, with an easy to remember name:

function stringinstring()
    case "$2" in 
          return 0
    return 1

To test if $string1 (say, abc) is contained in $string2 (say, 123abcABC) I just need to run stringinstring "$string1" "$string2" and check for the return value, for example

stringinstring "$str1" "$str2"  &&  echo YES  ||  echo NO

I use this function (one dependency not included but obvious). It passes the tests shown below. If the function returns a value > 0 then the string was found. You could just as easily return 1 or 0 instead.

function str_instr {
   # Return position of ```str``` within ```string```.
   # >>> str_instr "str" "string"
   # str: String to search for.
   # string: String to search.
   typeset str string x
   # Behavior here is not the same in bash vs ksh unless we escape special characters.
   str="$(str_escape_special_characters "${1}")"
   if [[ "${x}" != "${string}" ]]; then
      echo "${#x} + 1" | bc -l
      echo 0

function test_str_instr {
   str_instr "(" "'[email protected] (dev,web)'" | assert_eq 11
   str_instr ")" "'[email protected] (dev,web)'" | assert_eq 19
   str_instr "[" "'[email protected] [dev,web]'" | assert_eq 11
   str_instr "]" "'[email protected] [dev,web]'" | assert_eq 19
   str_instr "a" "abc" | assert_eq 1
   str_instr "z" "abc" | assert_eq 0
   str_instr "Eggs" "Green Eggs And Ham" | assert_eq 7
   str_instr "a" "" | assert_eq 0
   str_instr "" "" | assert_eq 0
   str_instr " " "Green Eggs" | assert_eq 6
   str_instr " " " Green "  | assert_eq 1

If you prefer the regex approach:

string='My string';

if [[ $string =~ .*My.* ]]
   echo "It's there!"

One is:

[ $(expr $mystring : ".*${search}.*") -ne 0 ] && echo 'yes' ||  echo 'no'

So there are lots of useful solutions to the question - but which is fastest / uses the least resource?

Repeated tests using this frame:

/usr/bin/time bash -c 'a=two;b=onetwothree; x=100000; while [ $x -gt 0 ]; do TEST ; x=$(($x-1)); done'

Replacing TEST each time:

[[ $b =~ $a ]]           2.92user 0.06system 0:02.99elapsed 99%CPU

[ "${b/$a//}" = "$b" ]   3.16user 0.07system 0:03.25elapsed 99%CPU

[[ $b == *$a* ]]         1.85user 0.04system 0:01.90elapsed 99%CPU

case $b in *$a):;;esac   1.80user 0.02system 0:01.83elapsed 99%CPU

doContain $a $b          4.27user 0.11system 0:04.41elapsed 99%CPU

(doContain was in F. Houri's answer)

And for giggles:

echo $b|grep -q $a       12.68user 30.86system 3:42.40elapsed 19%CPU !ouch!

So the simple substituion option predicatbly wins whether in an extended test or a case. The case is portable.

Piping out to 100000 greps is predictably painful! The old rule about using external utilities without need holds true.

This Stack Overflow answer was the only one to trap space and dash chars:

# For null cmd arguments checking   
to_check=' -t'
space_n_dash_chars=' -'
[[ $to_check == *"$space_n_dash_chars"* ]] && echo found

This also works:

if printf -- '%s' "$haystack" | egrep -q -- "$needle"
  printf "Found needle in haystack"

And the negative test is:

if ! printf -- '%s' "$haystack" | egrep -q -- "$needle"
  echo "Did not find needle in haystack"

I suppose this style is a bit more classic -- less dependent upon features of Bash shell.

The -- argument is pure POSIX paranoia, used to protected against input strings similar to options, such as --abc or -a.

Note: In a tight loop this code will be much slower than using internal Bash shell features, as one (or two) separate processes will be created and connected via pipes.

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!"

Note that spaces in the needle string need to be placed between double quotes, and the * wildcards should be outside.

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"

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).