TechEarl

How to grep Case-Insensitively (grep -i)

Use grep -i for case-insensitive matching, including recursive, literal and multiple-pattern searches. Includes locale caveats and PowerShell equivalents.

Ishan Karunaratne⏱️ 12 min readUpdated
Share thisCopied
How to grep case-insensitively with grep -i. Combine -i with -r, -w, -v, -c, the locale caveat for non-ASCII case folding, the PCRE (?i) inline flag, and BSD vs GNU grep differences.

grep -i 'pattern' file matches the pattern regardless of case. With -i, error, Error, ERROR, and eRRoR all match the same search. It is one of the most-used grep flags because log files, error messages, and code identifiers are rarely consistent about capitalization.

The flag is short for --ignore-case. It works in every regex mode (grep, grep -E, grep -F, grep -P) and combines cleanly with the other flags you reach for daily. Case folding depends on the locale and implementation, so check the locale when searching multilingual text. That caveat gets its own section below.

Set your values

Try it with your own values

Set your OS, search path, and pattern. The interactive command blocks below update with your values.

The one-liner

bash· Linux (GNU)
grep -i ':pattern' app.log

That prints every line in app.log that contains :pattern in any mix of upper and lower case. Note the PowerShell line has no extra flag: Select-String is case-insensitive by default, the opposite of grep. To make Select-String case-sensitive you add -CaseSensitive.

The pattern is quoted with single quotes so the shell does not expand or interpret it. Quote your grep patterns always, even when they look like plain words today, because the next edit might add a * or a $.

Ignore case for literal text or multiple patterns

Use -F when the search text should be literal. Without it, the dot in error.code is a regex wildcard:

bash
grep -Fin -e 'error.code' app.log

Use multiple -e options to match any of several patterns, ignoring case:

bash
grep -in -e 'error' -e 'warning' app.log

Add -F to treat every pattern as literal text. -n prints line numbers. These fixed examples are independent of the values panel above.

In scripts, GNU grep returns 0 when a line matches, 1 when nothing matches, and 2 on an error. A no-match result is not a broken command. With -q, a match can produce status 0 even if an error also occurred. See GNU grep's exit status reference.

For recursive PowerShell searches, pipe Get-ChildItem -Recurse -File into Select-String; Select-String itself has no -Recurse parameter. See Microsoft's Select-String reference.

Combining -i with the flags you already use

-i stacks with the rest of grep's flag set. The combinations below are the ones I type most often.

The single most useful combination. Search an entire directory tree, ignoring case.

bash· Linux (GNU)
grep -rin -- ':pattern' ':search_path'

-r is recursive, -i ignores case, -n prints line numbers. This is the everyday "find every mention of this thing anywhere in the project" search. Add -I (capital i, distinct from -i) to skip binary files, which both GNU and BSD grep support.

-i with -w: whole word, any case

-w constrains the match to whole words, so :pattern does not match a substring of a longer identifier. Combined with -i, you get a case-insensitive whole-word search.

bash· Linux (GNU)
grep -iw ':pattern' app.log

Without -w, searching for error also matches errored, terror, and error_handler. With -iw, only the standalone word error, Error, or ERROR matches. PowerShell has no -w flag, so the word boundaries go into the regex as \b. See grep whole word matching for the full word-boundary reference.

-i with -v: invert, ignoring case

-v inverts the match: it prints lines that do not match. With -i, the exclusion is case-insensitive too.

bash· Linux (GNU)
grep -iv ':pattern' app.log

This prints every line that has no :pattern in it, regardless of how it was capitalized. Useful for trimming a known-noisy string out of output: grep -iv 'debug' drops every line mentioning debug in any case.

-i with -c: count matches, ignoring case

-c prints a count of matching lines instead of the lines themselves.

bash· Linux (GNU)
grep -ic ':pattern' app.log

One subtlety: -c counts matching lines, not total matches. A line with :pattern mentioned three times counts once. To count every occurrence including repeats on a line, use grep -io ':pattern' app.log | wc -l, where -o prints each match on its own line.

Case-insensitive recursive search across a codebase

Putting the combination to work: find every case-insensitive mention of a string in a project, with line numbers, skipping node_modules and .git.

bash· Linux (GNU)
grep -rin --exclude-dir=node_modules --exclude-dir=.git ':pattern' :search_path

GNU grep and current macOS grep support --exclude-dir. The mac variant here uses find and xargs as another way to control which directories are traversed. For GNU-only features such as -P, brew install grep supplies ggrep. The grep cheat sheet covers the filename-filtering patterns in more depth.

The locale and UTF-8 caveat

This is the part most -i tutorials skip. -i reliably folds ASCII case only. For the 26 Latin letters A-Z, -i always treats upper and lower as equivalent. Outside ASCII, case folding depends on your locale.

The locale is set by environment variables, chiefly LC_ALL, LC_CTYPE, and LANG. Check it with:

bash
locale

If your locale is C or POSIX (common on minimal containers and stripped-down servers), grep does no non-ASCII case folding at all. With LC_CTYPE=C, grep -i 'eacute' will not match an accented E-acute against a lowercase e-acute, because the C locale knows nothing about accented characters.

A UTF-8 locale such as en_US.UTF-8 enables Unicode-aware case folding for accented Latin characters, Greek, Cyrillic, and most scripts that have a case distinction. To force it for one command:

bash
LC_ALL=en_US.UTF-8 grep -i 'pattern' file.txt

Two specific traps worth naming:

  • The Turkish dotted-i. In a Turkish locale (tr_TR.UTF-8), uppercase I lowercases to a dotless i, and lowercase i uppercases to a dotted capital. So grep -i 'I' behaves differently under tr_TR.UTF-8 than under en_US.UTF-8. If a script searches for INDEX and runs on a Turkish-locale host, the match for index can silently fail. This is the canonical case-folding bug.
  • The C locale on containers. Docker base images frequently ship with LANG unset, which defaults to the C locale. A grep -i that worked on your dev machine can quietly stop folding non-ASCII case inside the container. If a pipeline depends on non-ASCII matching, set LANG explicitly in the image.

For pure ASCII text, none of this matters and -i just works. The caveat only bites once your data has accented or non-Latin characters.

Per-pattern case control with PCRE (?i)

-i is all-or-nothing: it applies to the entire pattern. Sometimes you want part of a pattern case-insensitive and the rest case-sensitive. PCRE mode (grep -P, GNU only) supports the inline (?i) flag for exactly this.

The inline flag turns case-insensitivity on from that point in the pattern onward:

bash· Linux (GNU)
grep -P '(?i):pattern' app.log

You can also scope it to a group with (?i:...), which makes only that group case-insensitive and leaves the rest of the pattern case-sensitive. For example, a pattern that matches the word log in any case but requires the rest to be exact uses (?i:log) followed by the case-sensitive part. PowerShell's .NET regex engine understands (?i) and (?i:...) the same way, which is why the Windows variant above pairs -CaseSensitive with an inline (?i) to flip just the relevant span.

(?i) is the right tool when -i is too blunt. If you only ever need whole-pattern folding, plain -i is simpler and works in every mode.

macOS BSD grep vs GNU grep

-i itself is identical on both. The differences are in the flags you combine it with.

BehaviorGNU grepBSD grep (macOS default)
-i (ignore case)SupportedSupported, identical
--ignore-case long formSupportedSupported
-P (PCRE, needed for (?i))SupportedNot supported
(?i) inline flagWorks under -PNo -P, so unavailable
--exclude-dir (for -ri searches)SupportedSupported by current macOS grep; check older BSD versions
Non-ASCII case foldingLocale-dependent (UTF-8 locale)Locale-dependent (UTF-8 locale)
-w whole wordSupportedSupported

The practical takeaway: grep -i, grep -ri, grep -iw, grep -iv, and grep -ic all work the same on macOS and Linux. The PCRE-dependent (?i) requires a build with -P support; macOS system grep does not provide it. Install GNU grep with brew install grep and use ggrep for those examples.

Common mistakes

1. Assuming -i folds non-ASCII case. Case folding depends on your locale, including special cases involving the letter i. LC_ALL=C gives predictable ASCII case folding, but does not supply Unicode case folding. On a C-locale host (many containers), non-ASCII -i does nothing. Check locale before trusting a non-English -i search.

2. Forgetting the locale on containers. A grep -i that works locally can stop folding non-ASCII case inside Docker, where LANG is often unset. Set LANG in the image if the pipeline needs it.

3. The Turkish-i surprise. Under tr_TR.UTF-8, I and i do not fold to each other the way they do in English locales. Scripts that search for uppercase identifiers can fail on Turkish-locale hosts. Force LC_ALL=C or LC_ALL=en_US.UTF-8 for ASCII identifier searches that must be deterministic.

4. Adding -i to a pattern that already spans both cases. A pattern like [Ee]rror already matches Error and error. Adding -i on top is redundant and, worse, it also makes any other letters in the pattern case-insensitive, which may not be what you intended. Pick one mechanism: either -i for the whole pattern, or an explicit character class, not both.

5. Confusing -i with -I. Lowercase -i ignores case. Uppercase -I skips binary files. They are unrelated. grep -rI skips binaries; grep -ri ignores case; grep -riI does both.

6. Expecting -c to count occurrences. -c counts matching lines. Three matches on one line count as one. Use grep -io 'pattern' | wc -l for a true occurrence count.

When NOT to use -i

-i is the wrong choice when case is meaningful data:

  • Matching constants versus variables. In most languages, MAX_RETRIES is a constant and maxRetries is a variable. A case-insensitive search collapses the two. When you specifically want the SCREAMING_SNAKE_CASE constant, search case-sensitively so the lowercase variable does not pollute the results.
  • Distinguishing types from instances. User the class and user the instance variable are different things. Refactoring the class name with a case-insensitive grep will sweep up every instance variable too.
  • Environment variables versus values. PATH the variable and path in a comment are unrelated. A case-sensitive search keeps configuration audits accurate.
  • Acronyms and identifiers in code review. When you grep a codebase to find every definition of API or URL, case sensitivity is usually what you want, because the lowercase forms appear inside hundreds of unrelated identifiers.

The rule of thumb: use -i for human-written prose (logs, messages, documentation) where capitalization is inconsistent and meaningless. Drop -i for code identifiers, where capitalization is a deliberate signal.

See also

FAQ

The -i flag (short for --ignore-case) makes grep match the pattern regardless of letter case. With grep -i 'error', the lines error, Error, ERROR, and eRRoR all match. It works in every regex mode: plain grep, grep -E, grep -F, and grep -P.

Combine the flags: grep -ri 'pattern' .. The -r is recursive and -i ignores case; flag order does not matter. Add -n for line numbers (grep -rin) and -I, capital i, to skip binary files. On GNU grep you can also add --exclude-dir=node_modules to skip a directory.

The -i flag reliably folds case only for ASCII letters A to Z. Non-ASCII case folding depends on your locale. If your locale is C or POSIX, common on minimal containers, grep does no non-ASCII folding at all. Run locale to check, and force a UTF-8 locale for one command with LC_ALL=en_US.UTF-8 grep -i 'pattern' file.

They are unrelated flags. Lowercase -i ignores letter case when matching. Uppercase -I tells grep to skip binary files entirely, treating them as having no matches. You can use both at once: grep -riI 'pattern' . searches recursively, ignores case, and skips binaries.

Use PCRE mode with the inline (?i) flag: grep -P '(?i)log' file turns on case-insensitivity from that point onward. To scope it to one group, use (?i:...), which makes only that group case-insensitive and leaves the rest of the pattern case-sensitive. The -P flag is GNU-only; macOS BSD grep does not have it, so install GNU grep and use ggrep.

Yes. PowerShell's Select-String is case-insensitive by default, the opposite of grep. To make it case-sensitive you add the -CaseSensitive switch. So Select-String -Pattern 'error' already behaves like grep -i, and you only add a flag when you want case to matter.

For ASCII text the overhead is negligible. Case-insensitive matching in a UTF-8 locale can be measurably slower than a case-sensitive search on very large inputs, because Unicode case folding is more work per character. If you are searching gigabytes and the pattern is pure ASCII, running under LC_ALL=C with a case-insensitive ASCII pattern is the fastest path, though it disables non-ASCII folding.

TagsgrepCLILinuxmacOSBSDRegexShell Scripting

Found this useful? Pass it on.

Copied

Ishan Karunaratne

Systems and Network Architect · Chief Technology Officer

Systems and network architect and Chief Technology Officer with more than two decades designing, building, and running production software, cloud and network architecture, Linux systems, and the bare metal underneath them, and lately working AI into the stack. A US Army veteran who served in Operation Iraqi Freedom. What I write here is drawn from the full arc of that work, across architecture, engineering, and operations, not any single job.

Keep reading

Related posts

Use grep 'pattern' file | awk '{print $2}' to filter lines and print a specific column. awk field basics, custom separators with -F, multi-column output, grep -o and cut alternatives, and when awk alone replaces the pipe.

How to grep and Print a Specific Column (grep + awk)

grep filters lines, awk extracts fields. The classic pipe is grep 'pattern' file | awk '{print $2}'. This covers awk field basics ($1, $NF), custom separators with -F, multi-column output, the cases grep -o and cut cover on their own, and the fact that awk's own pattern match makes the grep half optional.

Generate an SSH key with ssh-keygen: pick Ed25519 over RSA, decide on a passphrase, and set the ~/.ssh permissions SSH needs. Linux, macOS and Windows.

How to Create an SSH Key in 2026

Create an SSH key in one ssh-keygen command. Which key type to pick in 2026 (Ed25519 vs RSA), whether to set a passphrase, and the file permissions SSH needs, on Linux, macOS and Windows.

Write an effective system prompt with five parts: role, capabilities, constraints, output, refusal. Before/after examples and the structure that maximises cache hits.

How to Write an Effective System Prompt

Write an effective system prompt for an LLM with five parts: role, capabilities, constraints, output format, refusal policy. With before/after examples and the structure that maximises cache hits.