The grep command

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.

Metacharacters in both grep and Bash itself can be confusing. We recommend you put your grep patterns in single quotes for clarity if they contain anything but numbers and letters, so that Bash will treat them as simple strings, and will pass them unchanged to grep.

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.