Reputation: 259
Is there any way to accomplish this without using external programs like sed
, grep
, awk
or the [[ ]]
construct and without using nested loops?
Basically, I would like to loop over the provided arguments(words) and check whether it's in the string.
By word, I mean actual tokens (IFS separated strings).
So, find word3 in string word1 word2 word3 word4
The only crappy solution I could come up with is:
string="word1 word2 word3 word4"
# attempt to remove diff combos of
# space + word
for i in $@; do
[ "${string/ $i / }" == "$string" ] &&
[ "${string/#$i / }" == "$string" ] &&
[ "${string/% $i/ }" == "$string" ] &&
echo "$i not found" && continue
# if found, actually remove
# again attempting diff combos
string="${string/ $i / }"
string="${string/#$i / }"
string="${string/% $i/ }"
done
echo "$string"
This should give:
~$ ./script word1 word3 word5
word5 not found
word2 word4
Upvotes: 0
Views: 3795
Reputation: 814
Modern Bash
$ a="qqq www eee rrr"
$ eval typeset -A t=( $(printf '[%s]=1 ' $a) )
$ w=www
$ [ "${t[$w]}" ] && echo yes || echo no
yes
$ w=oops
$ [ "${t[$w]}" ] && echo yes || echo no
no
Even though there is no apparent loop here, there is necessarilly one internally (the loading of the t[] array), then looking for a word is done by hashing.
There is no fork/exec since printf is a builtin here, when t[] is not needed anymore, unset it.
This answer is late :) I give it for the record.
Upvotes: 2
Reputation: 103
Use an intermediate array:
#!/bin/bash
string="word1 word2 word3 word4"
array=($string)
for i in "$@"; do
found=false
for j in "${!array[@]}"; do
if [[ $i == ${array[$j]} ]]; then
unset array[$j]
found=true
break
fi
done
"$found" || echo "$i not found" >&2
done
echo "${array[@]}"
The result is exactly what you were asking for:
$ ./script word1 word3 word5
word5 not found
word2 word4
Upvotes: 0
Reputation: 3474
#!/bin/bash
string="word1 word2 word3 word4"
# Usage: inarray "$value" "${array[@]}"
inarray() { local n=$1 h; shift; for h; do [ "$n" == "$h" ] && return; done; return 1; }
read -ra arr <<< "$string"
for arg; do
if inarray "$arg" "${arr[@]}"; then
printf '%s FOUND in "%s"\n' "$arg" "$string"
else
printf '%s NOT FOUND in "%s"\n' "$arg" "$string"
fi
done
Usage example:
(Script name is sof
)
./sof word1 wor word3 "word4 "
word1 FOUND in "word1 word2 word3 word4"
wor NOT FOUND in "word1 word2 word3 word4"
word3 FOUND in "word1 word2 word3 word4"
word4 NOT FOUND in "word1 word2 word3 word4"
EDIT I noticed that the OP requested to not use nested loops, which this answer does. Therefore it does not answers the question.I'll leave the answer here in case that someone will benefit from it in the future, unless I'll be requested to remove it.
Upvotes: 0
Reputation: 84531
You can always use the traditional expr
syntax. e.g. expr $string : $regex
. For example, using your input (and reformatting slightly), you could do:
#!/bin/bash --norc
string="word1 word2 word3 word4"
for i in $@; do
if [ $(expr "$string" : ".*$i.*") -gt 0 ]; then
printf "'%s' found in '%s'\n" "$i" "$string"
else
printf "'%s' NOT found in '%s'\n" "$i" "$string"
fi
done
Output
$ bash srchwordexpr.sh word1 word3 word5
'word1' found in 'word1 word2 word3 word4'
'word3' found in 'word1 word2 word3 word4'
'word5' NOT found in 'word1 word2 word3 word4'
expr
is not one of your excluded tools, whether you consider it to belong in that group, let me know.
You can also use a simple substring removal with the same results:
if [ "$string" != "${string/$i}" ]; then
Upvotes: 1