← Back to Bash Course | Chapter 7: String Manipulation | Lesson 10 of 10

Checking Substrings/Prefixes

Bash can check whether a string contains, starts with, or ends with a particular piece of text using either case or the [[ ]] test with glob wildcards.

Checking if a String Contains a Substring

[[ $str == *substring* ]] uses glob wildcards to check whether substring appears anywhere inside $str, since the unquoted * on each side matches any surrounding text (including none). This is the standard idiom for a simple contains check without needing grep.

Example: Checking if a String Contains a Substring

bash
#!/bin/bash
sentence="The quick brown fox jumps"
if [[ $sentence == *brown* ]]; then
    echo "Found 'brown' in the sentence"
fi

Checking a Prefix or Suffix

[[ $str == prefix* ]] checks that $str starts with prefix, and [[ $str == *suffix ]] checks that it ends with suffix, by placing the wildcard only on the side you don't care about. This reads naturally and avoids manual substring slicing just to compare the start or end.

Example: Checking a Prefix or Suffix

bash
#!/bin/bash
filename="report_2024.csv"
if [[ $filename == report_* ]]; then
    echo "Starts with 'report_'"
fi
if [[ $filename == *.csv ]]; then
    echo "Ends with '.csv'"
fi

Using case for Substring Checks

case can perform the same kind of glob-based substring, prefix, and suffix checks as [[ ]], which is especially readable when you're distinguishing between several different possible patterns at once. Each pattern branch is tried in order until one matches.

Example: Using case for Substring Checks

bash
#!/bin/bash
url="https://example.com/page"
case $url in
    https://*)
        echo "Secure URL"
        ;;
    http://*)
        echo "Insecure URL"
        ;;
    *)
        echo "Unknown URL scheme"
        ;;
esac

Quoted Patterns Disable Glob Matching

If the pattern on the right of == inside [[ ]] is quoted, Bash treats it as a literal string rather than a glob pattern, so wildcards like * lose their special meaning and become ordinary characters to match exactly. This distinction is important to remember since it flips the check from contains to 'is exactly equal to'.

Example: Quoted Patterns Disable Glob Matching

bash
#!/bin/bash
value="hello"
if [[ $value == h* ]]; then
    echo "Unquoted pattern: glob match, starts with 'h'"
fi
if [[ $value == "h*" ]]; then
    echo "This won't print"
else
    echo "Quoted pattern: treated as literal 'h*', no match"
fi
Common Mistakes
  1. Trying to use [[ $str == *substring* ]] with the pattern in quotes; quoting the pattern turns off glob matching entirely and forces a literal, exact-string comparison instead.
  2. Assuming case and [[ == ]] support regex-style anchors like ^/$; they use glob wildcards (*, ?, [...]), where * already implicitly means 'anything, including nothing, anywhere in that position'.
  3. Using [[ $str = *sub* ]] (single =) and being surprised it also does glob matching; inside [[ ]], both = and == behave identically for pattern matching, unlike inside [ ] where = is strictly a literal string comparison.
Chapter Summary
  • [[ $str == *substring* ]] checks if substring appears anywhere within $str, using unquoted glob wildcards.
  • [[ $str == prefix* ]] checks if $str starts with prefix; [[ $str == *suffix ]] checks if it ends with suffix.
  • case can achieve the same checks with pattern branches, which reads well when checking several possible substrings at once.
  • Quoting the pattern (e.g. "*substring*") disables glob matching and forces a literal, exact-string comparison -- the opposite of what you usually want here.

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.