zagortenay333
zagortenay333

Reputation: 259

Bash: Find word in string

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

Answers (4)

Phi
Phi

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

SamTheEagle
SamTheEagle

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

Rany Albeg Wein
Rany Albeg Wein

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

David C. Rankin
David C. Rankin

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

Related Questions