Grep

Imagine that someone asks you to find a string in a file. Easy, right? Open the file, press Ctrl+F (or Cmd+F if you prefer), type the string, and voila – your editor highlights the string for you. Isn’t modern technology incredible?

However, if the next request is to search five more files for the string, and then ten more files, well… that’s going to get awfully annoying. This kind of scenario is exactly what our next Unix command line tool, grep, is fantastic for. Its odd name actually comes from a different program, ed, which has a command to search through a file for a regular expression – g/re/p (globally search for regular expression ’re' and then print any matches). Let’s check out the man page for grep:

$ man grep | head -n 7
GREP(1)                                                User Commands                                               GREP(1)

NAME
       grep, egrep, fgrep - print lines that match patterns

SYNOPSIS
       grep [OPTION...] PATTERNS [FILE...]

View an online copy of the grep man page.

Note that you can provide multiple options (or none at all), followed by a ‘pattern’, and then several files. So grep will be able to search for whatever pattern we want in as many files as we want. Great! Also notice the square brackets [ and ] – that means those parameters are optional, so really, all grep needs is a pattern to search for. If we don’t supply any files, it’ll just read from stdin as we’d expect.

Grepping for a pattern on stdin

(that’s right, to use grep is called “grepping” ;-))

Let’s run grep with just a pattern, no files:

$ grep bananas
apples
bananas
bananas
whoa! it printed 'bananas'
whoa! it printed 'bananas'
but it won't print this line!
(there's no match)

Most implementations of grep will also highlight the matches in color if your terminal supports it.

Important: grep processes files line by line, so by default a matching substring will print the entire line.

Grepping files

Searching for a pattern in text we are typing ourselves is not very exciting. Let’s try this on a file. Say that I want to look up my user information in the Unix “password” file, /etc/passwd:

$ grep mmalensek /etc/passwd
mmalensek:x:1000:998::/home/mmalensek:/usr/bin/zsh

Wow, there I am! (Don’t worry, the password file doesn’t actually contain any passwords… what a great name ;-))

Let’s try a more general search:

$ grep ma /etc/passwd
mail:x:8:12::/var/spool/mail:/usr/bin/nologin
mmalensek:x:1000:998::/home/mmalensek:/usr/bin/zsh

Now it matches “ma” both for me and the mail user. Did I mention regular expressions before? That means we can use some special symbols to search for particular patterns in the text. Say that we want to search for any user with a name that starts with ‘r’:

$ grep '^r' /etc/passwd
root:x:0:0::/root:/bin/bash

You will note that I enclosed the pattern in small quotes so that my shell doesn’t try to interpret the ^ character and instead just passes it on to grep. Many special characters will need to be quoted so that the shell doesn’t process them. For example, if you want to search for the pipe character (|) then you’d also need single quotes so the shell doesn’t think you’re writing a command pipeline. If in doubt, quote your pattern (or just do it all the time)!

Fun fact: you can use $ to search the end of the line: grep 'end$' will match anything that ends in… “end”!

Maybe we want to know the line number the match is on. We can use the -n flag:

# Search for lines that end in 'sh' and show line numbers:
$ grep -n 'sh$' /etc/passwd
1:root:x:0:0::/root:/bin/bash
18:mmalensek:x:1000:998::/home/mmalensek:/usr/bin/zsh

Grepping multiple files

We can specify as many files as we’d like after the pattern, like this:

$ grep '^r' /etc/passwd /etc/shadow /etc/hostname /etc/secret/dir/my_stuff.txt

In fact, grep can also recurse through subdirectories. So we can search our entire home directory for ‘cheesy poofs’ like this:

$ grep -r 'cheesy poofs' ~/
(countless instances of 'cheesy poofs' appear here)

Another useful thing we can do is ignore case with the -i flag:

$ grep -ri 'cheesy poofs' ~/
(countless instances of 'cheesy poofs' appear here, including 'CHEESY POOFS')

# This is equivalent:
$ grep -r -i 'cheesy poofs' ~/

You can also invert the search with the -v flag to make grep match all the lines that do not contain the pattern.

Moving on

This doesn’t even begin to scratch the surface when it comes to using grep – it’s an extremely powerful tool, both in its command line options as well as all the things you can search for with regular expressions.

Grep is based on highly efficient algorithms for searching text; if you have some spare time, The Treacherous Optimization is a great read.