7

I wanna test whether a string has "substring". Most answers online is based on Bash. I tried

if [ $string == "*substring*" ] 

which was not working. Currently

if echo ${string} | grep -q "substring" 

worked. Is there any other better way.

3
  • 1
    Your original attempt requires bash's builtin [[, so probably won't work for you. Commented Nov 23, 2016 at 18:03
  • yeah, so do you know any better approach? Commented Nov 23, 2016 at 18:07
  • 2
    Nope. All improvements I can think of would only work in Bash. There is really nothing wrong with using grep -q though, except that is starts a separate subprocess. Commented Nov 23, 2016 at 18:09

4 Answers 4

10

Using POSIX compliant parameter-expansion and with the classic test-command.

#!/bin/sh

substring=ab
string=abc

if [ "$string" != "${string%"$substring"*}" ]; then
    echo "$substring present in $string"
fi

(or) explicitly using the test operator as

if test "$string" != "${string%$substring*}" ; then
Sign up to request clarification or add additional context in comments.

3 Comments

I'm not sure how to make this completely robust, though. You need to at least quote the expansion of $substring for this to work for string=a*b and substring=*, for example. [ "$string" != "${string%"$substring"*}" ] (This might be sufficient, though.)
@chepner: Thanks! Updated!
Also keep in mind that regex meta characters like '*' need quoting in $substring.
6

In a POSIX-features only shell you won't be able to do general pattern or regex matching inside a conditional without the help of an external utility.

That said:

  • Kenster's helpful answer shows how to use the branches of a case ... esac statement for pattern matching.

  • Inian's helpful answer shows how to match indirectly inside a conditional, using patterns as part of parameter expansions.

Your own grep approach is certainly an option, though you should double-quote ${string}:

if echo "${string}" | grep -q "substring"; ...

A slightly more efficient way is to use the expr utility, but note that per POSIX it is limited to BREs (basic regular expressions), which are limited:

string='This text contains the word "substring".'

if expr "$string" : ".*substring" >/dev/null; then echo "matched"; fi

Note that the regex - the 3rd operand - is implicitly anchored at the start of the input, hence the need for .*.

>/dev/null suppresses expr's default output, which is the length of the matched string in this case. (If nothing matches, the output is 0, and the exit code is set to 1).

Comments

3

If you're just testing for substrings (or anything that can be matched using filename wildcards) you can use case:

#!/bin/sh
while read line; do
    case "$line" in
    *foo*) echo "$line" contains foo ;;
    *bar*) echo "$line" contains bar ;;
    *)     echo "$line" isnt special ;;
    esac
done

$ ./testit.sh
food
food contains foo
ironbar
ironbar contains bar
bazic
bazic isnt special
foobar
foobar contains foo

This is basic Bourne shell functionality. It doesn't require any external programs, it's not bash-specific, and it predates POSIX. So it should be pretty portable.

1 Comment

++ for a true POSIX-compliant pattern-matching option that I've missed in my answer (it now links to yours too).
0

Short answer is no, not if you are trying to use vanilla sh, without Bash extensions. On many modern systems, /bin/sh is actually a link to /bin/bash, which provides a superset of sh's functionality (for the most part). Your original attempt would have worked with Bash's builtin [[ extended test command: http://mywiki.wooledge.org/BashFAQ/031

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.