When you need to find an error line in a log of several thousand lines,
look for a keyword in source code or filter the output of a command, grep
is the essential tool. Simple and powerful, grep searches for text
patterns (plain or regex) and prints the matching lines, straight from the
command line.
What you will learn
- Search for a simple pattern in a file
- Use the essential options -i, -v, -n, -c
- Show the context with -A, -B, -C
- Use basic and extended (-E) regular expressions
- Run recursive searches with -r
The grep command in the Linux ecosystem
grep belongs to the text-processing commands of Linux. Each tool has its
speciality, and grep is the one that selects lines:
| Command | Speciality | When to use it |
|---|---|---|
grep | Filter lines by pattern | Search, filter, diagnose |
awk | Advanced column processing | Calculations, conditions, complex formats |
sed | Edit text as a stream | Replace, delete, insert |
cut | Extract columns | Delimited files (CSV, TSV) |
find | Search for files | Find by name, size, date |
xargs | Turn stdin into arguments | Chain commands over lists |
When to use grep vs awk?
The table below shows where grep is enough and where awk takes over,
as soon as you need columns or calculations:
| Situation | grep | awk |
|---|---|---|
| Search for a word in a file | Yes, grep "mot" fichier | Yes, but overkill |
| Ignore case | Yes, grep -i "mot" | Yes, tolower() |
| Show the lines containing a pattern | Yes, perfect | Yes |
| Extract a specific column | No | Yes, awk '{print $2}' |
| Count exact occurrences | No, -c counts lines | Yes |
| Calculations on the matched lines | No | Yes |
| Complex conditions (AND, OR, IF) | Watch out: limited | Yes |
Frequent combinations:
# grep + cut: filter then extract a columngrep 'ERROR' app.log | cut -d' ' -f4
# grep + sort + uniq: count the types of errorsgrep 'ERROR' app.log | cut -d':' -f2 | sort | uniq -c
# grep + xargs: act on the files containing a patterngrep -rl 'TODO' src/ | xargs -I{} echo "Fichier à traiter: {}"
# grep + wc: count the matching linesgrep -c 'ERROR' *.log | grep -v ':0$'Understand the grep command in 2 minutes
The mental model below sums up how grep works: an input, a
pattern, the matching lines as output.
🧠 Mental model: How grep works
grep = Set the input → Give the pattern → Get the matching lines
Key points
- grep filters the lines containing a pattern (text or regex)
- Without a file, grep reads from stdin (pipe)
- By default, the search is case-sensitive
- -i to ignore case, -v to invert (lines without the pattern)
- -n prints line numbers, -c counts the matching lines
- grep -E enables extended regex (|, +, ?, etc.); egrep is obsolescent
Golden rules
Essential vocabulary
- -i
- Ignore case (case-insensitive)
- -v
- Invert: lines WITHOUT the pattern
- -n
- Print line numbers
- -c
- Count lines (not occurrences)
- -r
- Recursive search in directories
- -l
- Print file names only
📚 Going further: 6 advanced options
- -E
- Extended regex (replaces egrep)
- -w
- Whole word only
- -o
- Print only the match
- -A N
- N lines after each match
- -B N
- N lines before each match
- -C N
- N lines of context (before and after)
grep reads the file or stdin line by line, tests whether the pattern is
present, then prints the matching lines on stdout.
Minimal syntax
The order of the three elements is not negotiable: the options come before
the pattern, and the pattern before the files. If you swap pattern and
file, grep treats your pattern as a file name, and you get a confusing
No such file or directory error. When [FICHIER...] is missing, grep reads
its standard input, which is exactly the behaviour you want in a pipeline.
grep [OPTIONS] MOTIF [FICHIER...]Search modes
grep offers several modes depending on the type of pattern. The choice comes
down to a single question: does your pattern contain characters that have a
special meaning in regex? If so and you want them literal, -F is the
safest answer. If on the contrary you want to use alternation or
quantifiers, -E spares you from escaping half the pattern.
| Mode | Command | Use | Example |
|---|---|---|---|
| Plain text | grep | Literal patterns | grep "ERROR" log.txt |
| Basic regex (BRE) | grep | Basic metacharacters | grep "^[0-9]" file |
| Extended regex (ERE) | grep -E | |, +, ?, () without escaping | grep -E "a|b" |
| Literal text | grep -F | Disables regex | grep -F "*.txt" file |
Essential options
These eight options cover almost every daily use. They combine freely:
grep -rin "timeout" /var/log/ stacks recursion, ignored case and line
numbers. Two of them change the nature of the output rather than the selection:
-c returns a number, -l returns paths. They are the ones you chain in a
script, because their output is easily consumed by another command.
| Option | Effect | Example |
|---|---|---|
-i | Ignore case | grep -i "error" |
-v | Invert (lines without the pattern) | grep -v "DEBUG" |
-n | Print line numbers | grep -n "ERROR" |
-c | Count lines | grep -c "ERROR" |
-l | Print only file names | grep -l "TODO" |
-r | Recursive search | grep -r "TODO" src/ |
-w | Whole word only | grep -w "port" |
-o | Print only the match | grep -o "[0-9]*" |
Show the context
An isolated error line rarely says what happened. These three options bring back
the neighbouring lines and turn a raw message into a readable sequence. One
detail to know: when two matches are close, grep inserts a -- line between
the context blocks to mark the gap. This line pollutes downstream
processing, remove it with --no-group-separator if you chain the output.
| Option | Effect | Example |
|---|---|---|
-A N | N lines after (After) | grep -A 3 "ERROR" |
-B N | N lines before (Before) | grep -B 2 "ERROR" |
-C N | N lines before and after (Context) | grep -C 2 "ERROR" |
# See what happened before and after an errorgrep -C 3 "Exception" application.log
# Show the context after each error (stack trace)grep -A 10 "ERROR" app.logBasic regular expressions
These patterns belong to the BRE (Basic Regular Expression) dialect, the one
grep uses without any option. Two behaviours surprise there: * does not mean
"anything" as in the shell, but "zero or more times the previous character";
and word boundaries are written \< and \>, with backslashes: a GNU
extension, absent from the POSIX standard. Remember the two anchors ^ and
$ first, they are the ones that remove the most false positives.
| Pattern | Meaning | Example |
|---|---|---|
^motif | Start of line | grep "^ERROR" |
motif$ | End of line | grep "done$" |
. | Any single character | grep "err.r" |
* | Zero or more of the previous one | grep "ab*c" |
[abc] | One of a, b or c | grep "[aeiou]" |
[^abc] | None of a, b or c | grep "[^0-9]" |
\<mot\> | Whole word (BRE) | grep "\<port\>" |
Extended regular expressions (-E)
With -E, you switch to ERE (Extended Regular Expression) and the advanced
metacharacters become available without escaping:
| Pattern | Meaning | Example |
|---|---|---|
a|b | a OR b | grep -E "ERROR|WARNING" |
+ | One or more of the previous one | grep -E "[0-9]+" |
? | Zero or one of the previous one | grep -E "colou?r" |
(abc) | Grouping | grep -E "(ab)+" |
{n,m} | Between n and m repetitions | grep -E "[0-9]{2,4}" |
# Find ERROR or WARNING or CRITICALgrep -E "ERROR|WARNING|CRITICAL" app.log
# Find the lines with a 2 to 4 digit numbergrep -E "[0-9]{2,4}" data.txt
# Extract the IP addressesgrep -oE "[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}" access.logRecursive search in a project
Recursion is the mode where grep can become slow and noisy. Two options settle
almost everything: --exclude-dir to skip dependency directories and the Git
history, --include to target only one file type. Without them, a search at
the root of a Node project walks through node_modules and drowns the useful
result. Note also that -r follows the symbolic links given as arguments
but not those met along the way; use -R if you want to follow them all.
# Every file of the current directorygrep -r "TODO" .
# With line numbersgrep -rn "FIXME" src/
# File names onlygrep -rl "password" /etc/
# Exclude some directoriesgrep -r "TODO" --exclude-dir={node_modules,.git} .
# Filter by extensiongrep -r "import" --include="*.py" .Typical errors (and fixes)
All these situations share the same signature: grep does not crash, it simply
returns an unexpected result. That is what makes them costly, since nothing
reports the error. The reflex that avoids almost all of them is to test the
pattern on a single known line before running it on a whole file.
| Error | Cause | Fix |
|---|---|---|
| No result | Different case | Use -i |
| Partial results | Substring match | Use -w for a whole word |
| Regex not recognised | Basic grep | Use -E for extended regex |
binary file matches | Binary file detected | Use -a to force text |
| grep finds itself | ps | grep | Add | grep -v grep |
Common search patterns
Now that you understand the logic of grep, here are sixteen ready-to-use
patterns. Each one gives its general formula and a concrete example.
Simple search
Find the lines containing a word. The output shows the whole line, not only
the matching part; the return code is 0 if at least one line matches, 1
otherwise, which makes this form usable as is in a script test.
grep "MOTIF" <fichier> # formulagrep "ERROR" application.log # exampleMOTIF: text or regular expression to search for
Ignore case
Search without telling upper and lower case apart. It is the first option to
reach for when an empty search surprises you although you are sure of the
content: log levels are written ERROR by some, Error by others.
grep -i "MOTIF" <fichier> # formulagrep -i "error" logs.txt # example-i: case-insensitive search
Exclude a pattern
Print the lines that do not contain the pattern, thanks to -v, the
inverted selection.
grep -v "MOTIF" <fichier> # formulagrep -v "DEBUG" application.log # example-v: inverts the selection
Count the lines
Count the number of matching lines. Watch the exact meaning: -c counts
lines, not occurrences. A line containing the pattern three times only
counts as one.
grep -c "MOTIF" <fichier> # formulagrep -c "ERROR" application.log # example-c: returns only the count, not the lines
With line numbers
See on which lines the matches sit, with -n.
grep -n "MOTIF" <fichier> # formulagrep -n "ERROR" application.log # example-n: prefixes each line with its number
Context after the match
See N lines after each match. It is the form suited to exception traces, where the error message fits on one line but the call stack unfolds on the following ones.
grep -A N "MOTIF" <fichier> # formulagrep -A 3 "Exception" error.log # example-A N: N lines after (After)
Context before the match
See N lines before each match. Useful in the opposite case: understand what the service was trying to do just before the failure, information that is always missing from the error line itself.
grep -B N "MOTIF" <fichier> # formulagrep -B 2 "FAILURE" job.log # example-B N: N lines before (Before)
Context before and after
See N lines on both sides of each match, with -C, the context
option.
grep -C N "MOTIF" <fichier> # formulagrep -C 2 "timeout" network.log # example-C N: N lines of context (Context)
Whole word only
Avoid partial matches. Without this option, looking for port in a
configuration also brings back import, export and support, which
drowns the useful result under unrelated lines.
grep -w "MOTIF" <fichier> # formulagrep -w "port" config.conf # example-w: enforces a word boundary
Recursive search
Search in every file of a directory. On a real project, always add
--exclude-dir: without it, grep goes down into .git and into the
dependency directories, and the search becomes both slow and unreadable.
grep -r "MOTIF" <dossier> # formulagrep -r "TODO" ./src/ # example-r: goes down into subdirectories
File names only
List the files containing the pattern, without printing the lines, with
-l.
grep -l "MOTIF" <fichiers> # formulagrep -rl "password" /etc/ # example-l: prints only the file paths
Extended regex (alternation)
Search for several patterns with an OR. Without -E, the vertical bar is
taken literally and grep looks for the literal string ERROR|WARNING, which
exists nowhere: you get zero results without the slightest error message.
grep -E "MOTIF1|MOTIF2" <fichier> # formulagrep -E "ERROR|WARNING|CRITICAL" app.log # example-E: enables extended regex|: alternation (OR)
Print only the match
Extract only the matching part, not the whole line, with -o: the basis
of any extraction.
grep -o "PATTERN" <fichier> # formulagrep -oE "[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+" access.log # example-o: prints only the match
Exclude comments
Ignore the comment lines and the empty lines of a configuration file.
grep -v "^#" <fichier> | grep -v "^$" # formulagrep -v "^#" config.conf | grep -v "^$" # example^#: lines starting with#^$: empty lines
Filtering pipeline
Filter the output of another command. It is the most common use in
administration. Remember that grep only sees the standard output here:
error messages slip past the filter until you add 2>&1.
<commande> | grep "MOTIF" # formulaps aux | grep nginx # examplegrepfilters what arrives on its standard input
Multi-file search
Search in several files at once. As soon as there is more than one file, grep
prefixes each line with the file name. This prefix is what lets you sort
or count per file afterwards, but it breaks processing that expected the raw
line.
grep "MOTIF" <fichier1> <fichier2> ... # formulagrep "server" *.conf # example*: shell wildcard to target several files
Pitfalls to avoid
Eight errors come back constantly with grep. For each one: the faulty
command, the symptom, the cause and the correction.
Unquoted pattern
An unquoted pattern is read by the shell before grep, which changes its
meaning.
grep *.log fichier.txt # WRONG: the shell interprets the * before grepSymptom: error or unexpected results. Cause: the shell interprets the
special characters (*, ?, |) before grep sees them.
grep -F "*.log" fichier.txt # RIGHT: protected from the shell, and read literallyQuotes are enough to keep the pattern away from the shell, but not to make
it literal: in "*.log", the . is still a metacharacter that accepts any
character. To search for exactly the string *.log, add -F.
Forgetting -i for case
By default, grep respects case, and log levels vary from one
application to another.
grep "error" fichier.log # WRONG: does not find "ERROR" or "Error"Symptom: lines are missing from the results. Cause: by default, grep
is case-sensitive.
grep -i "error" fichier.log # RIGHT: case ignoredgrep finds itself
The grep process shows up in the ps list it is filtering.
ps aux | grep nginx # WRONG: also prints the line of the "grep nginx" processSymptom: a stray line containing grep in the results. Cause: the
grep process itself appears in the ps list.
ps aux | grep nginx | grep -v grep # RIGHT: the grep line is excludedBasic versus extended regex
The vertical bar only means OR in extended regex.
grep "ERROR|WARNING" fichier.log # WRONG: looks for the literal text "ERROR|WARNING"Symptom: no result. Cause: in basic regex, | is not alternation, you
must escape it or use -E.
grep -E "ERROR|WARNING" fichier.log # RIGHT: extended regexUnescaped special characters
A metacharacter in a pattern you meant as literal text changes the search.
grep "prix: 10$" fichier.txt # WRONG: the $ is read as "end of line"Symptom: unexpected results. Cause: $ is a regex metacharacter.
grep -F "prix: 10$" fichier.txt # RIGHT: -F treats the pattern as literal text-c counts lines, not occurrences
-c answers "how many lines", not "how many occurrences".
grep -c "mot" fichier.txt # WRONG: returns 2 even if "mot" appears 5 times on 2 linesSymptom: a count lower than the real number of occurrences. Cause: -c
counts the lines containing the pattern, not the total number of matches.
grep -o "mot" fichier.txt | wc -l # RIGHT: counts the real occurrencesBinary files skipped
A file with a null byte is treated as binary, and grep hides its
lines.
grep "motif" fichier.bin # WRONG: prints "grep: fichier.bin: binary file matches" instead of the contentSymptom: a message instead of the result. Cause: grep detects a
binary file (a single null byte is enough) and does not print its content.
Since grep 3.5, this message goes to standard error: a 2>/dev/null
redirection makes it disappear, and the command then seems to find nothing.
grep -a "motif" fichier.bin # RIGHT: -a forces text processingUnwanted partial match
grep searches for a substring, not a word.
grep "port" config.conf # WRONG: also finds "import", "export", "support"Symptom: too many irrelevant results. Cause: grep looks for the
substring, not the whole word.
grep -w "port" config.conf # RIGHT: -w enforces the whole wordHands-on lab
Nothing replaces practice. Create the test environment below, then go
through the twelve steps. At the end, clean up with rm -rf ~/grep-lab.
Prepare the ground
This script creates four test files in ~/grep-lab: an application
log, a configuration file, Python source code and a contact list.
# Create the labmkdir -p ~/grep-labcd ~/grep-lab
# Application log filecat > application.log << 'EOF'2025-01-15 08:00:01 INFO Application démarrée2025-01-15 08:00:02 DEBUG Connexion base de données établie2025-01-15 08:01:15 INFO Utilisateur alice connecté2025-01-15 08:02:30 WARNING Mémoire utilisée: 75%2025-01-15 08:03:45 ERROR Timeout lors de la requête API2025-01-15 08:03:46 DEBUG Retry 1/3 en cours...2025-01-15 08:03:48 ERROR Timeout lors de la requête API2025-01-15 08:03:49 DEBUG Retry 2/3 en cours...2025-01-15 08:03:51 INFO Requête réussie après retry2025-01-15 08:05:00 INFO Utilisateur bob connecté2025-01-15 08:10:00 WARNING Disque utilisé: 85%2025-01-15 08:15:00 ERROR Permission denied: /var/data/secret.txt2025-01-15 08:20:00 INFO Batch job terminé avec succèsEOF
# Configuration filecat > config.conf << 'EOF'# Configuration serveurserver.host=localhostserver.port=8080server.ssl.enabled=true# Assistancesupport.email=support@example.com# Base de donnéesdatabase.host=db.example.comdatabase.port=5432database.name=productiondatabase.user=admin# Cachecache.enabled=truecache.ttl=3600# Logginglog.level=INFOlog.file=/var/log/app.logEOF
# Python source codecat > app.py << 'EOF'#!/usr/bin/env python3"""Application principale"""import osimport logging
logging.basicConfig(level=logging.INFO)logger = logging.getLogger(__name__)
def connect_database(host, port): logger.info(f"Connexion à {host}:{port}") # TODO: implémenter la connexion réelle return True
def process_data(data): # FIXME: gérer les erreurs if not data: raise ValueError("Data is empty") return data.upper()
if __name__ == "__main__": connect_database("localhost", 5432) # TODO: ajouter la logique métierEOF
# File with email addressescat > contacts.txt << 'EOF'alice@example.com - Alice Martinbob.smith@company.org - Bob Smithcharlie@test.co.uk - Charlie Browninvalid-email - Non valideADMIN@EXAMPLE.COM - Administrateursupport@help-desk.net - Support techniqueEOF
echo "Correct : Lab créé dans ~/grep-lab"ls -la ~/grep-labThe twelve steps
Go through them in order: each step reuses a notion from the previous one, and the last four combine several options. After each command, compare what you get with the explanation line: if the number of lines printed differs, it is almost always the pattern that is at fault, not the command. The lab files are deliberately short so that you can check the result by eye.
-
Simple search. Find every line containing
ERRORinapplication.log.Terminal window grep "ERROR" ~/grep-lab/application.loggrepprints every line containing the pattern, here three lines. -
Ignore case. Find every variant of
error, whatever the case.Terminal window grep -i "error" ~/grep-lab/application.log-imakes the search case-insensitive. -
Line numbers. Print the errors with their line number.
Terminal window grep -n "ERROR" ~/grep-lab/application.log-nprefixes each result with its line number in the file. -
Invert the search. Print every line except those containing
DEBUG.Terminal window grep -v "DEBUG" ~/grep-lab/application.log-vinverts the filter: it keeps the lines that do not match. -
Count the occurrences. Count the number of lines containing
INFO.Terminal window grep -c "INFO" ~/grep-lab/application.log-creturns the number of matching lines, not the lines themselves. -
Context after. Print each
ERRORfollowed by the next 2 lines.Terminal window grep -A 2 "ERROR" ~/grep-lab/application.log-A Nadds N lines of context after each match. For context before and after:grep -C 1 "ERROR" ~/grep-lab/application.log. -
Whole word. Find
INFOwithout catching a possibleINFORMATION.Terminal window grep -w "INFO" ~/grep-lab/application.log-wenforces a word boundary around the pattern. -
Basic regular expressions. Find the lines starting with a date (
2025-).Terminal window grep "^2025-" ~/grep-lab/application.log^stands for the start of line in regex. -
Extended regex with alternation. Find the lines containing
ERRORorWARNING.Terminal window grep -E "ERROR|WARNING" ~/grep-lab/application.log-Eenables extended regex, where|means "or". -
Exclude comments. Print the lines of
config.confthat are not comments.Terminal window grep -v "^#" ~/grep-lab/config.conf^#targets the lines starting with#;-vexcludes them. -
Pipeline with grep. List the running Python processes.
Terminal window ps aux | grep python | grep -v grepgrepfilters the output ofps; the secondgrep -v grepremoves the line of thegrepitself. -
Recursive search. Find every
TODOin the lab files.Terminal window grep -r "TODO" ~/grep-lab/-rwalks recursively through every file of the directory, theTODOs are inapp.py.
Progressive exercises
Ten exercises to validate your mastery. All of them work on the files of the
~/grep-lab lab. Search on your own first, then unfold the solution to
check yourself.
Foundation level
These four exercises only use a single option at a time. Do them without
looking at the tables above: the goal is to memorise the letter tied to each
effect, -i, -v and -c, because they are the ones you will type most often
in production.
Exercise 1, find the errors. Print every line containing ERROR in application.log. Hint: simple syntax grep "motif" fichier.
Show the solution
grep "ERROR" ~/grep-lab/application.loggrep prints the lines containing the given pattern.
Exercise 2, ignore case. Find every variant of warning (warning, WARNING, Warning…). Hint: -i for case-insensitive.
Show the solution
grep -i "warning" ~/grep-lab/application.log-i makes the search case-insensitive.
Exercise 3, lines without DEBUG. Print every line except those containing DEBUG. Hint: -v inverts the selection.
Show the solution
grep -v "DEBUG" ~/grep-lab/application.log-v prints the lines that do not match the pattern.
Exercise 4, count the errors. Count the number of lines containing ERROR. Hint: -c for count.
Show the solution
grep -c "ERROR" ~/grep-lab/application.log-c returns only the number of matching lines.
Composition level
One step up: these five exercises ask you to pick the right option among
several plausible candidates. Exercise 6 is the most instructive, since the
naive version without -w returns a result, but the wrong one. A non-empty
result is never proof that the pattern was correct.
Exercise 5, error context. Print each ERROR line with the next 2 lines. Hint: -A for after.
Show the solution
grep -A 2 "ERROR" ~/grep-lab/application.log-A N prints N lines after each match.
Exercise 6, whole word. Find port in config.conf without catching import or export. Hint: -w for word.
Show the solution
grep -w "port" ~/grep-lab/config.conf-w guarantees that the pattern is a whole word, not a substring: two lines come out (server.port and database.port), against three without the option, support.email containing port inside support.
Exercise 7, regex with alternation. Find the lines containing ERROR or WARNING. Hint: -E for extended regex, | for OR.
Show the solution
grep -E "ERROR|WARNING" ~/grep-lab/application.log-E enables extended regex, where | means "or".
Exercise 8, exclude comments. Print the lines of config.conf without the comments (#). Hint: ^# targets the start of line, -v inverts.
Show the solution
grep -v "^#" ~/grep-lab/config.conf^# targets the lines starting with #; -v excludes them.
Exercise 9, recursive search. Find every TODO in the lab files. Hint: -r for recursive.
Show the solution
grep -r "TODO" ~/grep-lab/-r walks recursively through every subdirectory.
Industrialisation level
Last exercise, and the only one that does not work on a file but on the output
of another command. It is the most frequent use of grep in
administration, and the one where the self-match trap fires every
time: the grep process you have just launched itself appears in the list
returned by ps.
Exercise 10, filter a process. List the processes containing ssh without printing the line of the grep itself. Hint: a second grep -v grep.
Show the solution
ps aux | grep ssh | grep -v grepThe second grep -v grep excludes the line of the grep process from the output.
Troubleshooting
When grep does not return what you expect, the problem almost always comes
from the pattern (case, regex, escaping). Here is how to diagnose it,
then how to read the error messages.
Diagnostic methods
The approach is to shrink the problem: instead of wondering why the pattern
fails on a ten-thousand-line file, test it on a test line that you build
with echo. If the pattern works there and not on the file, the pattern is
fine and it is the content that differs from what you imagine: non-breaking
spaces, Windows carriage returns, invisible characters. That is what
cat -A reveals, by printing ^M$ at the end of the line for a DOS-format
file.
# Test the pattern on a simple lineecho "test ERROR test" | grep "ERROR"
# Show the non-printable characters of the filecat -A fichier.log | head
# Validate a regular expression on its ownecho "test123" | grep -E "[0-9]+"
# See how many lines match before filteringgrep -c "MOTIF" fichier.logFrequent errors
Unlike the previous pitfalls, these cases produce an explicit message on
standard error. That is good news: the cause is identified by grep itself.
Always read the message in full, it contains the path or the faulty part of
the pattern, which saves you from looking in the wrong place.
| Error message | Probable cause | Fix |
|---|---|---|
grep: <fichier>: No such file or directory | Missing file or wrong path | Check with ls, review the relative/absolute path |
grep: Unmatched [, [^, [:, [., or [= | Opening bracket not closed (grep "[abc") | Close the bracket, or -F for a literal search |
grep: Unmatched ( or \( | Unclosed parenthesis in -E | Close the parenthesis, or escape it: \( |
grep: Invalid content of \{\} | Inconsistent interval, such as {2,1} | Write {min,max} with min lower than max |
grep: <fichier>: binary file matches | The file is detected as binary | Force text mode with grep -a |
grep cheat sheet
The two tables below condense the whole guide on one screen. The first gathers
the options of the command, the second the metacharacters of the
pattern. The distinction matters: an option goes before the pattern and changes
the behaviour of grep, a metacharacter lives inside the pattern and must be
protected by quotes so that the shell leaves it alone.
Syntax and options
Take this table as a starting list, not as a complete reference. The first
four rows cover most of daily work; the context options -A, -B and
-C become essential as soon as you read error traces, where the message
line is never enough to understand the sequence.
| Element | Role | Example |
|---|---|---|
grep "motif" fichier | Simple search | grep "ERROR" log.txt |
-i | Ignore case | grep -i "error" log.txt |
-v | Invert (exclude) | grep -v "DEBUG" log.txt |
-n | Line numbers | grep -n "ERROR" log.txt |
-c | Count lines | grep -c "ERROR" log.txt |
-w | Whole word | grep -w "port" config |
-l | File names only | grep -rl "TODO" . |
-r | Recursive search | grep -r "TODO" src/ |
-o | Print only the match | grep -o "IP: [0-9.]*" |
-A N | N lines after | grep -A 3 "ERROR" |
-B N | N lines before | grep -B 2 "ERROR" |
-C N | N lines of context | grep -C 2 "ERROR" |
-E | Extended regex | grep -E "a|b" |
-F | Literal text (no regex) | grep -F "*.txt" |
Regular expressions
Only one row of this table depends on an option: the alternation a|b only
makes sense with -E. All the others work in BRE, hence without an option.
If you had to remember only two entries, take the anchors ^ and $: they
are the ones that turn an approximate search into a precise filter, for example
to keep only the comment lines of a configuration file.
| Pattern | Role | Example |
|---|---|---|
^motif | Start of line | grep "^#" config |
motif$ | End of line | grep "done$" log |
. | Any single character | grep "err.r" |
* | Zero or more occurrences of the previous one | grep "ab*c" |
[abc] | One character from the list | grep "[aeiou]" |
[^abc] | Anything but these characters | grep "[^0-9]" |
a|b | a OR b (with -E) | grep -E "cat|dog" |
\<mot\> | Whole word (basic regex) | grep "\<port\>" |
Mastery checklist
You master grep when you can tick each of these points, from the simple
search to the pipeline and extended regex:
- I can run a simple search with
grep - I can ignore case with
-i - I can invert the search with
-v - I can count lines with
-c - I can show the context with
-A,-B,-C - I can search for a whole word with
-w - I can use extended regex with
-E - I can run a recursive search with
-r - I can fit
grepinto a pipeline
Conclusion
The grep command is the most used text-search tool on Linux. Its simple
syntax (grep "motif" fichier) covers 80% of daily needs. The options -i
(case), -v (invert), -n (numbers), -r (recursive) and -E
(extended regex) cover the advanced cases. For more complex processing (column
extraction, calculations), combine grep with awk or cut.
Articles you may find useful
Key points
grepprints the whole lines that contain the pattern; its return code is 0 if at least one matches, 1 otherwise.- The pattern always goes between quotes, otherwise the shell interprets
*,?and|beforegrep. -iignores case,-vinverts,-nnumbers,-wenforces the whole word.-ccounts lines, not occurrences:grep -o motif | wc -lcounts occurrences.- Without an option,
grepspeaks BRE: alternation|requires-E, and-Fdisables regex entirely. -A,-Band-Cbring back the context of an error, essential to read a trace.grep -rcombines with--exclude-dirand--includeto stay fast on a real project.
Knowledge check
Check that the essentials of this guide are acquired. The questions only cover what has just been explained here.
Knowledge check
Check what you have learned with this interactive quiz
How it works
- The timer starts when you click Start
- Multiple choice, true/false and short answer questions
- You can move between questions
- Detailed results are shown at the end
Starts the quiz and the timer
Review
(0/0)Score by domain
A domain groups several guides and gets several questions, so its score is more reliable than a per-topic score, which often rests on a single question.
Summary
Strengths
No topic passed at 80% on at least two questions yet.
Weak areas
No topic below the pass mark.
Recommended next
Hints before you try again?
Continue the course: jq: process JSON on the command line
A new full quiz with random questions
Work again only on the questions you missed