Grep is well known and commonly used command line search function that far too many people aren’t taking advantage of. The purpose of this article is to give technically inclined folks easy to follow examples to speed up work. I’ll also discuss speed concerns because no one wants to wait 8 seconds for a search to finish.

Getting your feet wet: single file search

Searching a single file for a string is about as simple as it gets so lets jump right into it. The syntax is:

grep [search string] [file]

Here’s an example

grep "header" log/dev.log

Responds with:

Dec 09 17:16:24 Send header "Content-Type: text/plain; charset=utf-8"
Dec 09 17:16:24 "Pragma: public: 1"
Dec 09 17:16:24 Send header "Content-Type: image/png"

Case insensitive search

Adding the -i flag to grep makes it search without case sensitivity

grep -i "HeAdEr" log/dev.log

Responds with:

Dec 09 17:16:24 Send header "Content-Type: text/plain; charset=utf-8"
Dec 09 17:16:24 "Pragma: public: 1"
Dec 09 17:16:24 Send header "Content-Type: image/png"

Learning something useful: recursive search

The previous example could be done casually in a text editor. Searching all the files in a directory is also doable but from some text editors (the ones I use at least) it’s not quite as easy. The -r flag indicates recursion but I’ll be combining it with the -i flag in the examples below to retain case insensitive search.

grep -ri "iboutlet" .  

Responds with:

./InspectAppDelegate.h:@property (nonatomic, retain) IBOutlet UIWindow *window;
./InspectAppDelegate.h:@property (nonatomic, retain) IBOutlet TemplateListTableViewController *templateListController;
./InspectAppDelegate.h:@property (nonatomic, retain) IBOutlet UINavigationController *navigationController;
./LoginViewController.h:    IBOutlet UITextField *usernameField;
./LoginViewController.h:    IBOutlet UITextField *passwordField;    
./LoginViewController.h:    IBOutlet UITextField *subdomainField;        
./LoginViewController.h:@property (nonatomic, retain) IBOutlet UITextField *usernameField;
./LoginViewController.h:@property (nonatomic, retain) IBOutlet UITextField *passwordField;
./LoginViewController.h:@property (nonatomic, retain) IBOutlet UITextField *subdomainField;
./ProjectAddViewController.h:@property (nonatomic, retain) IBOutlet UITextField *projectNameField;
./ProjectDetailsImageViewController.h:@property (nonatomic, retain) IBOutlet UIImageView *imageView;
./ProjectDetailsImageViewController.h:@property (nonatomic, retain) IBOutlet UIImage *image;
./ProjectDetailsTextEditViewController.h:@property (nonatomic, retain) IBOutlet UITextView *answerField;
./ProjectDetailsTextEditViewController.h:@property (nonatomic, retain) IBOutlet UILabel *fieldName;

Special cases: getting a list of files

Recursive search is great but it can give you tons of results from the same file which isn’t ideal in all cases (eg: log files). Getting a list of the files which contain matches rather than a list of matches can be done by adding the -l flag

grep -ril "iboutlet" .

Responds with:

./InspectAppDelegate.h
./LoginViewController.h
./ProjectAddViewController.h
./ProjectDetailsImageViewController.h
./ProjectDetailsTextEditViewController.h

But that’s not all: additional flags

Grep is a mature and powerful tool and as expected it has far more options and uses than i’m going to go into here but I do feel compelled to list a few more flags that may be helpful.

-A 20

Prints 20 (or whatever number you specify) lines after each match.

-B 20

Same thing as -A but before each match instead of after.

-C 40

Same thing as -B 20 -A 20 (C for context).

-n  or --line-number   

Adds the line number of the match to the results.

-v or --invert-match     

Invert match (get all the results that don’t match the pattern provided).

A need for speed: making common searches faster within project directories

If you’re on OS X and you’re looking for a fast case insensitive search within a directory that shows only file names (grep -ril “pattern”) you should be using mdfind (spotlight on command line).

mdfind -onlyin . "iboutlet"

Responds with:

/Users/pauldenya/full/path/to/LoginViewController.h
/Users/pauldenya/full/path/to/ProjectDetailsTextEditViewController.h
/Users/pauldenya/full/path/to/ProjectDetailsImageViewController.h
/Users/pauldenya/full/path/to/ProjectAddViewController.h

For those times when you’re not working on OS X a pruned find function can work wonders for improving slow search in large project directories. The idea is that in many situations, not all directories under the project directory need to be searched so we come up with a list of directories that do need to be searched and put them in a function in your profile.

#this goes in ~/.bashrc or /etc/profile
function projectfind() {
    grep -ril "$1" /path/to/project/search/me
    grep -ril "$1"  /path/to/project/search/here/too
    grep -ril "$1"  /path/to/project/web/js
}
projectfind "iboutlet"

This approach is project specific which isn’t great if you’re often switching between or starting new projects. You could change “/path/to/project/” to “./” but then you’d always need to be in the root project directory for search to work. The solution here is to pipe files to search to grep and use grep to prune the list in line. Here’s an example for a function that will search within the current directory but ignore log files and css directories:

#this goes in ~/.bashrc or /etc/profile
function myfind() {
   find . | grep -vE "\.log$|css" | xargs grep "$1" 
}

You can add additional logic by adding to OR clauses to the regex used in the first grep statement or adding additional grep statements.