Match a whole word
Match a whole word with the GNU boundary escape, the -w option or the angle-bracket word anchors. All three treat the low line as a word character.
Searching for cat finds concat too, and you want only the word on its own.
words=$(printf '%s\n' cat concat cat_food cat-food 'the cat sat')
echo "$words" | grep -n '\bcat\b'
echo ---
echo "$words" | grep -nw cat
echo ---
echo "$words" | grep -n '\<cat\>'
echo ---
echo "$words" | grep -nP '(?<![A-Za-z])cat(?![A-Za-z])'1:cat
4:cat-food
5:the cat sat
---
1:cat
4:cat-food
5:the cat sat
---
1:cat
4:cat-food
5:the cat sat
---
1:cat
3:cat_food
4:cat-food
5:the cat satA word boundary is a position between a word character, a letter, a digit or a low line, and a non-word character. The first three forms are equivalent in GNU grep, so cat_food is rejected because the low line counts as part of its word, while cat-food is accepted. The lookaround pattern in the last block defines a word as letters only and so accepts cat_food as well. The anchors are described in the GNU grep manual.
The boundary escape and the angle-bracket anchors are extensions of GNU grep and sed and are not in POSIX. BSD and macOS spell them with bracketed word-start and word-end classes, while -w is portable. Digits are word characters too, so cat2 does not match, and letters outside ASCII depend on the locale.