52

I want to see if a string is inside a portion of another string.
e.g.:

'ab' in 'abc' -> true
'ab' in 'bcd' -> false

How can I do this in a conditional of a bash script?

0

7 Answers 7

49

[[ "bcd" =~ "ab" ]]
[[ "abc" =~ "ab" ]]

the brackets are for the test, and as it is double brackets, it can so some extra tests like =~.

So you could use this form something like

var1="ab"
var2="bcd"
if [[ "$var2" =~ "$var1" ]]; then
    echo "pass"
else
    echo "fail"
fi

Edit: corrected "=~", had flipped.

2
  • 1
    I get fail with this parameters: var2="abcd"
    – Lucio
    May 25, 2013 at 0:02
  • 3
    @Lucio The correct is [[ $string =~ $substring ]]. I updated the answer. May 25, 2013 at 0:38
30

You can use the form ${VAR/subs} where VAR contains the bigger string and subs is the substring your are trying to find:

my_string=abc
substring=ab
if [ "${my_string/$substring}" = "$my_string" ] ; then
  echo "${substring} is not in ${my_string}"
else
  echo "${substring} was found in ${my_string}"
fi

This works because ${VAR/subs} is equal to $VAR but with the first occurrence of the string subs removed, in particular if $VAR does not contains the word subs it won't be modified.

6
  • I think that you should change the sequence of the echo statements. Because I get ab is not in abc
    – Lucio
    May 25, 2013 at 0:05
  • You are right! :P
    – edwin
    May 25, 2013 at 0:06
  • Mmm.. No, the script is wrong. Like that I get ab was found in abc, but if I use substring=z I get z was found in abc
    – Lucio
    May 25, 2013 at 0:08
  • 1
    Now I get ab is not in abc. But z was found in abc. This is funny :D
    – Lucio
    May 25, 2013 at 0:11
  • 1
    Duh! The echoes were right at the start of this! XD
    – edwin
    May 25, 2013 at 0:13
12

Using bash filename patterns (aka "glob" patterns)

substr=ab
[[ abc == *"$substr"* ]] && echo yes || echo no    # yes
[[ bcd == *"$substr"* ]] && echo yes || echo no    # no
1
  • if [[ "$JAVA_OPTS" != "-XX:+UseCompressedOops" ]]; then export JAVA_OPTS="$JAVA_OPTS -XX:+UseCompressedOops"; fi
    – Mike Slinn
    Feb 10, 2016 at 17:41
10

The following two approaches will work on any POSIX-compatible environment, not just in bash:

substr=ab
for s in abc bcd; do
    if case ${s} in *"${substr}"*) true;; *) false;; esac; then
        printf %s\\n "'${s}' contains '${substr}'"
    else
        printf %s\\n "'${s}' does not contain '${substr}'"
    fi
done
substr=ab
for s in abc bcd; do
    if printf %s\\n "${s}" | grep -qF "${substr}"; then
        printf %s\\n "'${s}' contains '${substr}'"
    else
        printf %s\\n "'${s}' does not contain '${substr}'"
    fi
done

Both of the above output:

'abc' contains 'ab'
'bcd' does not contain 'ab'

The former has the advantage of not spawning a separate grep process.

Note that I use printf %s\\n "${foo}" instead of echo "${foo}" because echo might mangle ${foo} if it contains backslashes.

1
  • First version works perfectly for finding substring of monitor name in list of xrandr monitor names stored in variable. +1 and welcome to 1K rep club :) Feb 10, 2018 at 16:31
6

shell case statement

This is the most portable solution, will work even on old Bourne shells and Korn shell

#!/bin/bash
case "abcd" in
    *$1*) echo "It's a substring" ;;
    *) echo "Not a substring" ;;
esac

Sample run:

$ ./case_substr.sh "ab"                                                                                           
It's a substring
$ ./case_substr.sh "whatever"                                                                                     
Not a substring

Note that you don't have to specifically use echo you can use exit 1 and exit 0 to signify success or failure.

What we could do as well, is create a function (which can be used in large scripts if necessary) with specific return values ( 0 on match, 1 on no match):

$ ./substring_function.sh                                  
ab is substring

$ cat substring_function.sh                                
#!/bin/sh

is_substring(){
    case "$2" in
        *$1*) return 0;;
        *) return 1;;
    esac
}

main(){
   if is_substring "ab" "abcdefg"
   then
       echo "ab is substring"
   fi
}

main $@

grep

$ grep -q 'ab' <<< "abcd" && echo "it's a substring" || echo "not a substring"                                    
it's a substring

This particular approach is useful with if-else statements in bash. Also mostly portable

AWK

$ awk '$0~/ab/{print "it is a substring"}' <<< "abcd"                                                             
it is a substring

Python

$ python -c 'import sys;sys.stdout.write("it is a substring") if "ab" in sys.stdin.read() else exit(1)' <<< "abcd"
it is a substring

Ruby

$ ruby -e ' puts "is substring" if  ARGV[1].include? ARGV[0]'  "ab" "abcdef"                                             
is substring
6
  • +1 for going above and beyond everyone else. I noticed here and on other stack exchange sites no answer returns the substring's offset within the string. Which is tonight's mission :) Nov 17, 2018 at 23:36
  • @WinEunuuchs2Unix Going to do that in bash ? Nov 17, 2018 at 23:38
  • Yes and No. I'm doing a Frankenstein project where python get's all the gmail.com messages metadata and bash parses it and presents a GUI list with drill down. I found the answer though here: stackoverflow.com/questions/5031764/… Nov 17, 2018 at 23:42
  • @WinEunuuchs2Unix OK. Sounds interesting. I'd personally prefer to parse everything in Python. It has far more capabilities for text processing than bash alone. Nov 17, 2018 at 23:46
  • I know your preferences for about two years and I respect them. But I'm just learning Python and getting yad to work within it seems cumbersome to me. Not to mention all the array processing which I'm already comfortable with in bash. But at least I wrote my first python script to suck everything out of google's gmail.com into Linux flat file right? :) Nov 17, 2018 at 23:49
5

Mind the [[ and ":

[[ $a == z* ]]   # True if $a starts with an "z" (pattern matching).
[[ $a == "z*" ]] # True if $a is equal to z* (literal matching).

[ $a == z* ]     # File globbing and word splitting take place.
[ "$a" == "z*" ] # True if $a is equal to z* (literal matching).

So as @glenn_jackman said, but mind that if you wrap the whole second term in double quotes, it will switch the test to literal matching.

Source: http://tldp.org/LDP/abs/html/comparison-ops.html

4

Similar to edwin's answer, but with improved portability for posix & ksh, and a touch less noisy than Richard's:

substring=ab

string=abc
if [ "$string" != "${string%$substring*}" ]; then
    echo "$substring IS in $string"
else
    echo "$substring is NOT in $string"
fi

string=bcd
if [ "$string" != "${string%$substring*}" ]; then
    echo "$string contains $substring"
else
    echo "$string does NOT contain $substring"
fi

Output:

abc contains ab
bcd does NOT contain ab

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.