Write a function that checks if a string starts with or contains something
check_prefixes () {
value=$1
for prefix in aa abc 3@3; do
case $value in
"$prefix"*) return 0
esac
done
return 1
}
check_contains_gt () {
value=$1
case $value in
*">"*) return 0
esac
return 1
}
var='aa>'
if check_prefixes "$var" && check_contains_gt "$var"; then
printf '"%s" contains ">" and starts with one of the prefixes\n' "$var"
fi
I divided the tests up into two functions. Both use case ... esac
and returns success (zero) as soon as this can be determined. If nothing matches, failure (1) is returned.
To make the list of prefixes more of a dynamic list, one could possibly write the first function as
check_prefixes () {
value=$1
shift
for prefix do
case $value in
"$prefix"*) return 0
esac
done
return 1
}
(the value to inspect is the first argument, which we save in value
and then shift
off the list of arguments to the function; we then iterate over the remaining arguments) and then call it as
check_prefixes "$var" aa abc 3@3
The second function could be changed in a similar manner, into
check_contains () {
value=$1
shift
case $value in
*"$1"*) return 0
esac
return 1
}
(to check for some arbitrary substring), or
check_contains_oneof () {
value=$1
shift
for substring do
case $value in
*"$substring"*) return 0
esac
done
return 1
}
(to check for any of a number of substrings)
For bash:
Using the properties of regex you can write start
with ^
and contain
by nothing.
The list of regexes to check start with aa
abc
or 3@3
and contains >
is:
^aa ^abc ^3@3 >
Make that a properly quoted list and ask bash to use regexes (=~
):
check_func() {
matched=1
for test_regex in '^aa' '^abc' '^3@3' '>'; do
if [[ $var =~ $test_regex ]] ; then
matched=0
break
fi
done
return "$matched"
}
var='aaIsAMatch'
if check_func; then
echo "A match was found"
fi
The function has hard-coded the list of matches and the name of the var.
Giving the list of regex in an array variable and the value to test on the first argument:
check_func() {
local matched; matched=1
for t in "${test_regex[@]}"; do
[[ $1 =~ $t ]] && { matched=0; break; }
done
return "$matched"
}
test_regex=('^aa' '^abc' '^3@3' '>')
if check_func 'aaIsAMatch'; then
echo "A match was found"
fi
The function could be further improved to use the name of a variable (instead of a value) as the first argument.
posix
As there is no regex in posix shells and the only way to test is a case statement, we must use a case statement. Sadly, for older shells ([no extended globs available][1]) we must loop to make all tests. And, the globs need to be:
'aa*' 'abc*' '3@3*' '*>*'
An script example that tests several input strings against several globs:
check_func() { :
matched=1
value=$1; shift
for t in "$@"; do
case $value in $t) matched=0; #break;; esac
echo "matched $value with $t"
;;
esac
done
return "$matched"
}
for var in abdg wabcde aadef abcde 3@3hello hmm3@3hell 'we>we' 'a>dfff' 'dfd>' 'a> de' 'a*> fg'; do
if check_func "$var" 'aa*' 'abc*' '3@3*' '*>*'; then
echo "========A match was found for \"$var\""
fi
done
A simpler version of the function to exactly match your request:
check_func() { :
matched=1
value=$1; shift
for t in "$@"; do
case $value in $t) matched=0; break;; esac
done
return "$matched"
}
Here's what the case
statement does: take the second parameter to the function ($2
). If it matches the pattern "$1"*
, i.e. the first argument to the function followed by anything, then execute true
and end the case
statement. true
does nothing and returns the status 0. Otherwise, if it matches *
, i.e. anything, execute false
and end the case
statement. false
does nothing and returns the status 1. Thus the case
statement has the status 0 if the second parameter starts with the first parameter and 1 otherwise. Since this is the last (and only) statement in the function, the function returns 0 if the second parameter starts with the first parameter and 1 otherwise.
Conditional statements such as if
in the shell consider a statement to be true if it returns 0 and false otherwise. Hence if beginswith "$var" "string"; then echo yes; else echo no; fi
prints yes
if the value of var
starts with string
and no
otherwise.
There are several alternative ways to write this function. For example the author could have used return 0
or return 1
instead of true
and false
, since they are the last statement in the function. The way the function was written makes it possible to use its body directly without wrapping it in a function, by just changing references to the function parameters ($1
and $2
) to whatever strings you want to work with.
To allow multiple prefixes, iterate over them in a loop. As soon as you've found a matching prefix, return from the function, with a true status (0). If none of the prefixes match, return a false status (conventionally 1).
# begins_with STRING PREFIX1 PREFIX2...
# Test if STRING starts with any of PREFIX1, PREFIX2, ...
begins_with () {
string=$1
shift
for prefix in "$@"; do
case "$string" in
"$prefix"*) return 0;;
esac
done
return 1
}
if begins_with "$var" 'aa' 'abc' '3@3'; then
echo "The value starts with one of the permitted prefixes"
fi
To test for a suffix, use the pattern *"$suffix"
instead of "$prefix"*
. To test for a substring, use *"$substring"*
. Note that the double quotes are necessary here, otherwise the variable would be interpreted as a pattern. For example:
suffix='?'
case "$var" in
*"$suffix") echo "The value of var ends with a question mark";;
esac
case "$var" in
*$suffix) echo "The value of var is not empty";;
esac