Most “bash string contains” tasks boil down to one of three checks: a literal
substring, a pattern (digits, letters, “only numbers”), or a prefix/suffix
on the string. Bash can do all of that inside [[ ... ]] with either glob
patterns or a regex match operator (=~). For ordering and equality without
pattern logic, see bash compare strings and
bash compare numbers when you move from
“contains” to “sorts before/after”.
Tested the
[[ ... ]]snippets from this article on Ubuntu 25.04, kernel 6.14.0-37-generic, Bash 5.2.37. One note on locale is in the last section.
Bash: check if a string contains another string (substring)
The usual bash if string contains pattern is a glob on the left-hand side and a quoted needle so empty values or spaces do not break the match:
haystack='foobar'
needle='oba'
if [[ $haystack == *"$needle"* ]]; then
echo 'substring found'
else
echo 'not found'
fiThat prints substring found. If needle is empty, the `` collapses in ways you
rarely want—guard with [[ -n $needle ]] before testing.
Bash: check if a string contains a digit, or only digits
To answer bash check if string contains a digit anywhere, [[ =~ ]] is concise:
VAR='test1234'
if [[ $VAR =~ [0-9] ]]; then
echo 'contains at least one digit'
else
echo 'no ASCII digits'
fiThat prints contains at least one digit. A plain hello run prints no ASCII digits.
For bash check if string is a number in the sense of “only digits” (an
integer-shaped string, allowing a leading 0), anchor the regex:
VAR='1234'
if [[ $VAR =~ ^[0-9]+$ ]]; then
echo 'only digits'
else
echo 'not only digits'
fi42 prints only digits; 42a prints not only digits. You can spell the class as
^[[:digit:]]+$ if you prefer POSIX character classes.
These tests care about decimal digits in the string, not whether the value fits in
an int for arithmetic—use $(( ... )) validation separately when you need numeric
range checks.
Bash string starts with or ends with a prefix
bash string starts with checks are globs on the left:
s='prefix_rest'
if [[ $s == prefix* ]]; then
echo 'starts with prefix'
fiEnds with uses a trailing glob:
f='access.log'
if [[ $f == *.log ]]; then
echo 'ends with .log'
fiBoth snippets print the success branch on the values shown.
Alphanumeric-only strings and “special” characters
To see if a value is letters and digits only (no spaces or punctuation), use
[[:alnum:]] anchored to the ends:
VAR='abc123'
if [[ $VAR =~ ^[[:alnum:]]+$ ]]; then
echo 'alnum only'
else
echo 'contains other characters'
fiabc@123 hits the second branch. The inverse—bash check if string contains
character classes outside alnum—is often written as “not alnum-only”:
VAR='abc@123'
if [[ ! $VAR =~ ^[[:alnum:]]+$ ]]; then
echo 'has non-alphanumeric characters'
fiThat prints has non-alphanumeric characters.
Vowels and other letter classes
A small bash check if string contains character example is vowel detection:
VAR='Apple'
if [[ $VAR =~ [AEIOUaeiou] ]]; then
echo 'contains a vowel'
else
echo 'no vowels matched'
fiThat prints contains a vowel; rhythm prints no vowels matched with this simple
ASCII set.
Locale, regex variables, and habits that save time
[[ string =~ regex ]]treats the right side as a regex unless it is stored in a variable—then the variable’s contents are the pattern. If you build patterns dynamically, keep them in a variable to avoid surprises with quoting.- Locale changes how
[[:alnum:]]treats letters outside ASCII. For strict ASCII tests, run withLC_ALL=Cfor that snippet. On the test host,cafématched^[[:alnum:]]+$under the default UTF-8 locale, but not when forced withLC_ALL=C—worth remembering if you script for international text. - For heavy validation (email, IDs), consider a small Python or
grep -Ehelper; nested regex in Bash gets hard to read fast.
Summary
Use [[ $haystack == *"$needle"* ]] for the common bash string contains
substring case, always quoting the needle. Use [[ $var =~ ... ]] when you need
bash check if string contains a digit ([0-9]), bash check if string is
number-like with ^[0-9]+$, or letter classes such as vowels. Use [[ $s == prefix* ]] or [[ $s == *.suffix ]] for bash string starts with or ends
with checks. For “only letters and digits”, ^[[:alnum:]]+$ is the usual test,
and negating it catches broader “non-alnum” content. Match your locale to the
data you expect, especially outside ASCII.

