Matt
Matt

Reputation: 2681

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

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

Upvotes: 189

Views: 340154

Answers (15)

Mike
Mike

Reputation: 9

To check if a variable contains a fixed-string i'm using this method :

if [ "$ALPHABET" != "${ALPHABET#*CDDE}" ]; then
    echo "the ALPHABET contains 'CDDE' string"
else
    echo "there is no 'CDDE' string in ALPHABET variable"
fi

To understand it more easily :

#!/bin/sh

LANG=C

ALPHABET="AABBCCDDEE"

EXAMPLE_1="${ALPHABET#*B}"
EXAMPLE_2="${ALPHABET##*B}"
EXAMPLE_3="${ALPHABET%B*}"
EXAMPLE_4="${ALPHABET%%B*}"

# The flag '#'  operates for the shortest prefix
# The flag '##' operates for the longest prefix
# The flag '%'  operates for the shortest suffix
# The flag '%%' operates for the longest suffix

echo $EXAMPLE_1 # BCCDDEE
echo $EXAMPLE_2 # CCDDEE
echo $EXAMPLE_3 # AAB
echo $EXAMPLE_4 # AA

Upvotes: 0

Shtefan
Shtefan

Reputation: 808

STRING="String to test"
SUBSTRING="test"

if [ $(echo $STRING|grep $SUBSTRING|wc -l) -ne 0 ]
then
    echo "exists"
fi

Upvotes: 0

David Tam
David Tam

Reputation: 331

Since I didn't see the example of how I've done it... Here's my contribution.

#!/bin/sh
thiscmd="java $@"
javasubstr=" -jar "
if [ -z "${thiscmd##*$javasubstr*}" ]; then
  #it exists
fi

Upvotes: 0

fjarlq
fjarlq

Reputation: 2549

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

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

A functionalized version with some tests:

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

testcontains() {
    testnum="$1"
    expected="$2"
    string="$3"
    substring="$4"
    contains "$string" "$substring"
    result=$?
    if [ $result -eq $expected ]; then
        echo "test $testnum passed"
    else
        echo "test $testnum FAILED: string=<$string> substring=<$substring> result=<$result> expected=<$expected>"
    fi
}

testcontains  1 1 'abcd' 'e'
testcontains  2 0 'abcd' 'ab'
testcontains  3 0 'abcd' 'bc'
testcontains  4 0 'abcd' 'cd'
testcontains  5 0 'abcd' 'abcd'
testcontains  6 1 '' 'a'
testcontains  7 0 'abcd efgh' 'cd ef'
testcontains  8 0 'abcd efgh' ' '
testcontains  9 1 'abcdefgh' ' '
testcontains 10 0 'abcd [efg] hij' '[efg]'
testcontains 11 1 'abcd [efg] hij' '[effg]'
testcontains 12 0 'abcd *efg* hij' '*efg*'
testcontains 13 0 'abcd *efg* hij' 'd *efg* h'
testcontains 14 1 'abcd *efg* hij' '*effg*'
testcontains 15 1 'abcd *efg* hij' '\effg\'
testcontains 16 0 'a\b' '\'
testcontains 17 0 '\' '\'
testcontains 18 1 '[' '\'
testcontains 19 1 '\' '['
testcontains 20 0 '-n' 'n'
testcontains 21 1 'n' '-n'
testcontains 22 0 '*\`[]' '\`'

Upvotes: 228

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

Upvotes: 1

bmargulies
bmargulies

Reputation: 100133

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

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

Upvotes: 6

Kemin Zhou
Kemin Zhou

Reputation: 6891

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.

Upvotes: 2

user324312
user324312

Reputation:

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.

Upvotes: 2

a.saurabh
a.saurabh

Reputation: 1231

#!/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

Upvotes: 55

ma77c
ma77c

Reputation: 1086

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

Upvotes: 10

John Hyland
John Hyland

Reputation: 6872

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.

Upvotes: 37

JoeOfTex
JoeOfTex

Reputation: 137

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.

Upvotes: 3

user5047266
user5047266

Reputation:

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

--> output: String 1 contain string 2

Upvotes: 2

helpermethod
helpermethod

Reputation: 62244

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

Upvotes: 20

Norman Ramsey
Norman Ramsey

Reputation: 202615

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.

Upvotes: 123

Related Questions