The grep command matches regular expression patterns against lines of input data. Regular expressions are a compact string syntax that can be used to describe patterns in text. They can be simple text strings, such as telnet, which will match any lines that have those six exact letters in sequence:
$ grep 'telnet' /etc/services telnet 23/tcp rtelnet 107/tcp # Remote Telnet rtelnet 107/udp telnets 992/tcp # Telnet over SSL tfido 60177/tcp # fidonet EMSI over telnet
We could use some regular expression properties to filter this data more precisely. For example, to limit the output only to lines that begin with the word telnet, we could use the ^ metacharacter:
$ grep '^telnet' /etc/services telnet 23/tcp telnets 992/tcp # Telnet over SSL
To limit the output to lines that end with telnet, we could use the $ metacharacter:
$ grep 'telnet$' /etc/services tfido 60177/tcp # fidonet EMSI over telnet
There are other metacharacters useful in even basic regular expressions. We'll look at more of them in detail in Chapter 6, Loops and Conditionals, including a way to match regular expressions in Bash itself, without using external tools.
By default, grep prints any lines in its input that match the pattern, in the same order in which it read them. It does not sort the output, or check it for uniqueness – you may need sort or uniq for that.
One of the common uses for grep in shell scripts is with the -q switch, which suppresses its output. This is useful for testing whether a file or input stream matches expressions without actually printing the matching lines, because grep exits with success (0) when it finds at least one pattern, and with failure (1) when it doesn't:
$ grep -q telnet /etc/services && echo 'Matched!' Matched! $ echo $? 0 $ grep -q foobar /etc/services && echo 'Matched!' $ echo $? 1
There are a few other useful switches to grep specified by POSIX that are worth mentioning.
The -c switch prints a count of the matching lines, and not the lines themselves:
$ grep -c telnet /etc/services 5
The -e switch allows you to specify multiple patterns to match in the text. If we wanted to match telnet or ssh in the services file, we could write that like this:
$ grep -e ssh -e telnet /etc/services
Note that the lines do not have to match both expressions to be printed; they only have to match one.
The -F switch allows you to search for simple text strings with no special meaning. This is one way to allow you to search for a literal dollar sign, for example, without it meaning "end of line" as it otherwise would:
$ grep -F '$' monthly-costs
Note that we still have to put it in single quotes, to stop Bash's special meaning for $ from interfering!
Finally, the -v switch can be used to invert the match, printing lines only if they don't match the pattern. For example, this prints the /etc/services file with no comment lines:
$ grep -v '^#' /etc/services
Most of the grep switches can be combined with one another. For example, to count all the lines in /etc/shells that don't have the bash or . fixed strings in them, we could write:
$ grep -cFv -e bash -e '.' /etc/shells 5
If you're using GNU/Linux, you are probably using GNU grep, which has many extended features; check out man grep to see them all.