145

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
2

3 Answers 3

199

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.

6
  • 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
    Commented Oct 23, 2014 at 15:39
  • 4
    Use echo "$line" to preserve existing newlines instead of creating ones with tr
    – Izkata
    Commented Oct 23, 2014 at 16:31
  • What is the availability of a Here String? Does it require Bash or another shell?
    – user56041
    Commented Jan 24, 2016 at 20:43
  • 1
    According to Wikipedia, here strings are available in bash, ksh, and zsh (but I'm assuming there may be others). Commented Jan 24, 2016 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
    Commented Jun 3, 2020 at 23:20
10
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...

shite

...but...

occur . . && echo yay || echo shite

...will print...

yay

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.

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

I would just use sed to break up the sentence for me and then compare each line in a loop because this seems like the easiest way to me (no offence to anyone here, and also I am not an expert so there may be a reason people don't do it this way, not sure)

string="a a a a a asd gsam en"
count=$(echo $string | sed 's/ /\n/g' | grep -w 'a' | wc -l)
echo $count

when I run it

mark@gamerblock:~$ string="a a a a a asd gsam en"
mark@gamerblock:~$ count=$(echo $string | sed 's/ /\n/g' | grep -w 'a' | wc -l)
mark@gamerblock:~$ echo $count
5

As a script with input argument

#!/bin/bash
echo "Enter your string"
read string
echo "Enter what you want substring counted"
read substring
count=$(echo $string | sed 's/ /\n/g' | grep $substring | wc -l)
echo $count

when ran

mark@gamerblock:~$ ./substring.sh
Enter your string
peter piper picked a pack of pickled peppers. a pack of pickled peppers peter piper did pick.
Enter what you want substring counted
peter
2
mark@gamerblock:~$ ./substring.sh
Enter your string
peter piper picked a pack of pickled peppers. a pack of pickled peppers peter piper did pick.
Enter what you want substring counted
p
0

If you don't want exact matches, remove the -w argument from grep

mark@gamerblock:~$ ./substring.sh
Enter your string
peter piper picked a pack of pickled peppers. a pack of pickled peppers peter piper did pick.
Enter what you want substring counted
p
12
mark@gamerblock:~$
5
  • 1
    This looks a lot like John WH Smith’s answer. OK, you’ve added grep -w, but you haven’t explained what that does. Commented Jul 1, 2022 at 2:46
  • The difference between my answer an Mr. Smith's is that the OP asked for how many matches in a sentence/string, and, to the best of my knowledge, the method using grep (as the OP requested) does not break the sentence into individual lines so that grep can go through it (which is why I used sed). Then wc -l was added to count the number of lines after the regex "select" was matched. The -w on grep makes it only match whole-word results, so something like "selection" won't trigger the filter Commented Jul 3, 2022 at 0:23
  • Sorry, upon re-reading, I didn't see the 'tr' command with grep initially. So the only difference in my answer and Mr. Smith's is the use of sed Commented Jul 3, 2022 at 0:26
  • There's too many 'e's in argument (twice) Commented Jul 8, 2022 at 19:24
  • Fixed, thanks Greenonline! Commented Jul 14, 2022 at 2:58

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .