Let's say I have a variable

line="This is where we select from a table."

now I want to grep how many times does select occur in the sentence.

grep -ci "select" $line

I tried that, but it did not work. I also tried

grep -ci "select" "$line"

It still doesn't work. I get the following error.

grep: This is where we select from a table.: No such file or directory

Have grep read on its standard input. There you go, using a pipe...

$ echo "$line" | grep select

... or a here string...

$ grep select <<< "$line"

Also, you might want to replace spaces by newlines before grepping :

$ echo "$line" | tr ' ' '\n' | grep select

... or you could ask grep to print the match only:

$ echo "$line" | grep -o select

This will allow you to get rid of the rest of the line when there's a match.

Edit: Oops, read a little too fast, thanks Marco. In order to count the occurences, just pipe any of these to wc(1) ;)

Another edit made after lzkata's comment, quoting $line when using echo.

  • 3
    Since the OP wants to count the number of occurrences of a string you can add a wc to complete the task: grep -o <string> <<< "$variable" | wc -l
    – Marco
    Oct 23 '14 at 15:39
  • 2
    Use echo "$line" to preserve existing newlines instead of creating ones with tr
    – Izkata
    Oct 23 '14 at 16:31
  • What is the availability of a Here String? Does it require Bash or another shell?
    – user56041
    Jan 24 '16 at 20:43
  • According to Wikipedia, here strings are available in bash, ksh, and zsh (but I'm assuming there may be others). Jan 24 '16 at 21:17
  • 1
    @Aubergine: copy-pastable examples - Filter-string is a variable: seq 4 | grep -v $SHLVL ; Filter-string from a different process: seq 4 | grep -v "$(seq 2)" Or seq 4 | grep -vf <(seq 2) ; Input from a different process: grep -v $SHLVL <(seq 4) Or grep -v $SHLVL <<< "$(seq 4)"
    – Amit Naidu
    Jun 3 '20 at 23:20
test=$line i=0
while case "$test" in (*select*)
test=${test#*select};;(*) ! :;;
esac; do i=$(($i+1)); done

You don't need to call grep for such a simple thing.

Or as a function:

occur() while case "$1" in (*"$2"*) set -- \
        "${1#*"$2"}" "$2" "${3:-0}" "$((${4:-0}+1))";;
        (*) return "$((${4:-0}<${3:-1}))";;esac
        do : "${_occur:+$((_occur=$4))}";done

It takes 2 or 3 args. Providing any more than that will skew its results. You can use it like:

_occur=0; occur ... . 2 && echo "count: $_occur"

...which prints the occurrence count of . in ... if it occurs at least 2 times. Like this:

count: 3

If $_occur is either empty or unset when it is invoked then it will affect no shell variables at all and return 1 if "$2" occurs in "$1" fewer than "$3" times. Or, if called with only two args, it will return 1 only if "$2" is not in "$1". Else it returns 0.

And so, in its simplest form, you can do:

occur '' . && echo yay || echo shite

...which prints...



occur . . && echo yay || echo shite

...will print...


You might also write it a little differently and omit the quotes around $2 in both the (*"$2"*) and "${1#*"$2"}" statement. If you do that then you can use shell globs for matches like sh[io]te for the match test.

  • 1
    +1: Faster, indeed. Works in bash and dash/sh (and probably a few others). Oct 23 '14 at 16:22
  • @JohnWHSmith - the edit is better, but still just as portable.
    – mikeserv
    Oct 23 '14 at 17:55

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.