Skip to main content

Command Palette

Search for a command to run...

My Deep Dive into the grep Command in Linux

Published
5 min read
My Deep Dive into the grep Command in Linux

For a long time, I kept seeing the grep command mentioned in tutorials and Stack Overflow threads, but it always felt abstract. I knew it searched inside files, yet I did not fully understand why developers used it so often. That changed when I needed it in my own project.

While working on my AESL inventory tracking system (React frontend and FastAPI backend), I had to trace where the word api appeared across the codebase. I was debugging route imports and configuration paths, and I wanted to find every occurrence of that keyword. That is when grep became useful.


My first encounter with grep

The first command I tried was:

grep -l "api" .

The terminal replied:

grep: .: Is a directory

That confused me. I expected grep to scan everything under the current folder and return files that contained api. The message taught me this: grep searches files, not directories, unless you tell it to search recursively. The . means the current directory, but I had not asked grep to descend into it.

So I retried with recursion:

grep -r "api" .

That produced lines from different files, for example:

./frontend/src/pages/SimpleDashboard.jsx:const apiUrl = ...
./frontend/src/services/api.js:export const getData = ...
./main.py:app.include_router(api_router)

At that point I saw grep as a practical way to inspect the whole project quickly.


Useful flags I relied on

After the initial success I explored more flags to refine searches.

-i — ignore case

grep -ir "api" .

Matches api, API, or Api. Useful when I am not sure about capitalization.

-l — show filenames only

grep -irl "api" .

Prints only the names of files that contain at least one match. This is handy when I just need to know where something exists.

-c — count matches

grep -irc "api" .

Prints path:count for each file. That gave me a sense of how widely the term appeared.

Example output:

./frontend/src/pages/SimpleDashboard.jsx:3
./frontend/src/services/api.js:5
./main.py:2

Note that -c counts matches per file, including zeroes, which is why I often filter out :0 later.

-v — invert match

grep -irv "api" .

Shows lines that do not contain api. This is useful to filter out noisy matches or to inspect everything except a pattern.

--include — target file types

If I only want certain file types, I use --include. A typical pattern:

grep -irl --include="*.py" "api" .

That limits the search to Python files and keeps the output focused.

One small but important detail: options should be placed before the pattern and path to avoid confusion. If you omit the leading hyphen on short flags or put arguments in the wrong order, the command can fail or behave unexpectedly.


Figuring out -l and -c

In my exploration I tried grep -ircl "api" . . I thought it would list filenames and show counts. That was incorrect as I noted only the file names were listed out without the count.

  • -i ignore case

  • -r recursive

  • -c count matches per file

  • -l list filenames only

When -l and -c are both present, -l takes precedence. In practice:

grep -ircl "api" .

prints only filenames that contain a match. It does not print counts. -l overrides -c because -l requests filenames only, and grep follows that output mode.

Why this happens

  • -c says: “Show me the count of matches in each file.”

  • -l says: “Only list the names of files that contain at least one match.”

If you want counts, do not include -l. For example:

grep -irc "api" .

prints path:count for all files. To show only files that actually have matches, filter out zeros:

grep -irc "api" . | grep -v ':0'

To get counts sorted by frequency, use:

grep -irc --exclude-dir={.git,node_modules} "api" . | grep -v ':0' | sort -t: -k2 -nr

This prints path:count sorted by count descending. If you only want filenames without counts, use:

grep -irl --exclude-dir={.git,node_modules} "api" .

Those alternatives let you get the output format you actually need.


A few practical details I used

  • Use --exclude-dir to skip heavy folders like .git and node_modules so searches are faster:

      grep -ir --exclude-dir={.git,node_modules} "api" .
    
  • Pipe into less for long outputs:

      grep -ir "api" . | less
    

    Press q to quit.

  • Combine --include with recursion to target file types:

      grep -ir --include="*.jsx" "api" frontend
    
  • If you want color highlighting inside less, force color and use less -R:

      grep -irE --color=always "api|component" . | less -R
    

What I took away

Learning grep was straightforward once I had a real problem to solve. The important bits I keep in mind are:

  • Always be explicit about recursion when you want to search directories.

  • Flags change output modes in ways that matter. -l and -c do not combine to give both names and counts. Pick one or post-process the output.

  • Use --include and --exclude-dir to narrow the search scope.

  • Pipe results to less, sort, awk, or grep again when you need different formatting.


Final note

That initial error message, grep: .: Is a directory, was helpful. It forced me to understand how grep expects arguments and how to control its behavior. Now I use grep as a practical inspection tool when I need to find references in a codebase.