How do you tell if a string contains another string in POSIX sh?

ShellUnix

Shell Problem Overview


I want to write a Unix shell script that will do various logic if there is a string inside of another string. For example, if I am in a certain folder, branch off. Could someone please tell me how to accomplish this? If possible I would like to make this not shell specific (i.e. not bash only) but if there's no other way I can make do with that.

#!/usr/bin/env sh

if [ "$PWD" contains "String1" ]
then
    echo "String1 present"
elif [ "$PWD" contains "String2" ]
then
    echo "String2 present"
else
    echo "Else"
fi

Shell Solutions


Solution 1 - Shell

Here's yet another solution. This uses POSIX substring parameter expansion, so it works in Bash, Dash, KornShell (ksh), Z shell (zsh), etc.

test "${string#*$word}" != "$string" && echo "$word found in $string"

A functionalized version with some examples:

# contains(string, substring)
#
# Returns 0 if the specified string contains the specified substring,
# otherwise returns 1.
contains() {
    string="$1"
    substring="$2"
    if test "${string#*$substring}" != "$string"
    then
        return 0    # $substring is in $string
    else
        return 1    # $substring is not in $string
    fi
}

contains "abcd" "e" || echo "abcd does not contain e"
contains "abcd" "ab" && echo "abcd contains ab"
contains "abcd" "bc" && echo "abcd contains bc"
contains "abcd" "cd" && echo "abcd contains cd"
contains "abcd" "abcd" && echo "abcd contains abcd"
contains "" "" && echo "empty string contains empty string"
contains "a" "" && echo "a contains empty string"
contains "" "a" || echo "empty string does not contain a"
contains "abcd efgh" "cd ef" && echo "abcd efgh contains cd ef"
contains "abcd efgh" " " && echo "abcd efgh contains a space"

Solution 2 - Shell

Pure POSIX shell:

#!/bin/sh
CURRENT_DIR=`pwd`

case "$CURRENT_DIR" in
  *String1*) echo "String1 present" ;;
  *String2*) echo "String2 present" ;;
  *)         echo "else" ;;
esac

Extended shells like ksh or bash have fancy matching mechanisms, but the old-style case is surprisingly powerful.

Solution 3 - Shell

#!/usr/bin/env sh

# Searches a subset string in a string:
# 1st arg:reference string
# 2nd arg:subset string to be matched

if echo "$1" | grep -q "$2"
then
    echo "$2 is in $1"
else 
    echo "$2 is not in $1"
fi

Solution 4 - Shell

Sadly, I am not aware of a way to do this in sh. However, using bash (starting in version 3.0.0, which is probably what you have), you can use the =~ operator like this:

#!/bin/bash
CURRENT_DIR=`pwd`

if [[ "$CURRENT_DIR" =~ "String1" ]]
then
 echo "String1 present"
elif [[ "$CURRENT_DIR" =~ "String2" ]]
then
 echo "String2 present"
else
 echo "Else"
fi

As an added bonus (and/or a warning, if your strings have any funny characters in them), =~ accepts regexes as the right operand if you leave out the quotes.

Solution 5 - Shell

case $(pwd) in
  *path) echo "ends with path";;
  path*) echo "starts with path";;
  *path*) echo "contains path";;
  *) echo "this is the default";;
esac

Solution 6 - Shell

Here is a link to various solutions of your issue.

This is my favorite as it makes the most human readable sense:

The Star Wildcard Method

if [[ "$string" == *"$substring"* ]]; then
    return 1
fi
return 0

Solution 7 - Shell

There's Bash regular expressions. Or there's 'expr':

 if expr "$link" : '/.*' > /dev/null; then
    PRG="$link"
  else
    PRG=`dirname "$PRG"`/"$link"
  fi

Solution 8 - Shell

If you want a ksh only method that is as fast as "test", you can do something like:

contains() # haystack needle
{
    haystack=${1/$2/}
    if [ ${#haystack} -ne ${#1} ] ; then
	    return 1
    fi
    return 0
}

It works by deleting the needle in the haystack and then comparing the string length of old and new haystacks.

Solution 9 - Shell

See the manpage for the 'test' program. If you're just testing for the existence of a directory you would normally do something like so:

if test -d "String1"; then
  echo "String1 present"
end

If you're actually trying to match a string you can use bash expansion rules & wildcards as well:

if test -d "String*"; then
  echo "A directory starting with 'String' is present"
end

If you need to do something more complex you'll need to use another program like expr.

Solution 10 - Shell

test $(echo "stringcontain" "ingcon" |awk '{ print index($1, $2) }') -gt 0 && echo "String 1 contain string 2"

--> output: String 1 contain string 2

Solution 11 - Shell

In special cases where you want to find whether a word is contained in a long text, you can iterate through the long text with a loop.

found=F
query_word=this
long_string="many many words in this text"
for w in $long_string; do
    if [ "$w" = "$query_word" ]; then
          found=T
          break
    fi
done
 

This is pure Bourne shell.

Solution 12 - Shell

This is another possible POSIX solution based on this answer, but making it work with special characters, like []*. This is achieved surrounding the substring variable with double quotes.

This is an alternative implementation of this other answer on another thread using only shell builtins. If string is empty the last test would give a false positive, hence we need to test whether substring is empty as well in that case.

#!/bin/sh
# contains(string, substring)
#
# Returns 0 if the specified string contains the specified substring,
# otherwise returns 1.
contains() {
    string="$1"
    substring="$2"
    test -n "$string" || test -z "$substring" && test -z "${string##*"$substring"*}"
}

Or one-liner:

contains() { test -n "$1" || test -z "$2" && test -z "${1##*"$2"*}"; }

Nevertheless, a solution with case like this other answer looks simpler and less error prone.

#!/bin/sh
contains() {
    string="$1"
    substring="$2"
    case "$string" in
        *"$substring"*) true ;;
        *) false ;;
    esac
}

Or one-liner:

contains() { case "$1" in *"$2"*) true ;; *) false ;; esac }

For the tests:

contains "abcd" "e" || echo "abcd does not contain e"
contains "abcd" "ab" && echo "abcd contains ab"
contains "abcd" "bc" && echo "abcd contains bc"
contains "abcd" "cd" && echo "abcd contains cd"
contains "abcd" "abcd" && echo "abcd contains abcd"
contains "" "" && echo "empty string contains empty string"
contains "a" "" && echo "a contains empty string"
contains "" "a" || echo "empty string does not contain a"
contains "abcd efgh" "cd ef" && echo "abcd efgh contains cd ef"
contains "abcd efgh" " " && echo "abcd efgh contains a space"

contains "abcd [efg] hij" "[efg]" && echo "abcd [efg] hij contains [efg]"
contains "abcd [efg] hij" "[effg]" || echo "abcd [efg] hij does not contain [effg]"

contains "abcd *efg* hij" "*efg*" && echo "abcd *efg* hij contains *efg*"
contains "abcd *efg* hij" "d *efg* h" && echo "abcd *efg* hij contains d *efg* h"
contains "abcd *efg* hij" "*effg*" || echo "abcd *efg* hij does not contain *effg*"

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionMattView Question on Stackoverflow
Solution 1 - ShellfjarlqView Answer on Stackoverflow
Solution 2 - ShellNorman RamseyView Answer on Stackoverflow
Solution 3 - Shella.saurabhView Answer on Stackoverflow
Solution 4 - ShellJohn HylandView Answer on Stackoverflow
Solution 5 - ShellhelpermethodView Answer on Stackoverflow
Solution 6 - Shellma77cView Answer on Stackoverflow
Solution 7 - ShellbmarguliesView Answer on Stackoverflow
Solution 8 - ShellJoeOfTexView Answer on Stackoverflow
Solution 9 - ShelljdesenoView Answer on Stackoverflow
Solution 10 - Shelluser5047266View Answer on Stackoverflow
Solution 11 - ShellKemin ZhouView Answer on Stackoverflow
Solution 12 - ShellCristóbal BorreroView Answer on Stackoverflow