Searching through text files is one of the most common tasks in Linux. Whether you're analyzing log files, finding specific code, or filtering data, the grep
command is your most powerful ally. In this comprehensive guide, you'll learn how to harness grep's pattern matching capabilities from the ground up.
π― What You'll Learn: In this hands-on tutorial, you'll discover:
- Understanding the grep command and its purpose
- Searching for exact text patterns
- Case-insensitive searching techniques
- Counting pattern matches in files
- Using regular expressions for advanced searches
- Matching patterns at line beginnings and endings
- Character class pattern matching
- Working with multiple vowels and complex patterns
- Real-world log file analysis
- Best practices and practical grep patterns
π Understanding grep
The grep
command stands for "Global Regular Expression Print". It searches through text files or input streams and prints lines that match a specified pattern.
Prerequisites
Before we dive in, you should have:
- Access to a Linux terminal or command line
- Basic understanding of text files
- Familiarity with creating and viewing files
Why Learn grep?
grep is essential for:
- Searching log files for errors or specific events
- Finding code in large codebases
- Filtering output from other commands
- Data analysis and text processing
- System administration and troubleshooting
π Setting Up Practice Files
Let's start by creating sample files to practice with. We'll create a simple text file with fruit names.
Creating a Sample Text File
echo -e "apple\nbanana\ncherry\ndate\nelderberry\nfig\ngrape" > sample.txt
What this command does:
echo -e
- Print text with escape sequence interpretation enabled- The
-e
flag allows us to use special characters like\n
- The
"apple\nbanana\n..."
- Text string with newlines\n
creates a new line for each fruit
> sample.txt
- Redirect output to a file- Creates
sample.txt
if it doesn't exist - Overwrites the file if it already exists
- Creates
Let's verify the file was created:
cat sample.txt
Output:
apple
banana
cherry
date
elderberry
fig
grape
What happened:
- Each fruit name appears on its own line
- We now have a 7-line text file to practice grep commands
π Basic Pattern Matching
The most basic use of grep is searching for an exact text pattern in a file.
Searching for Exact Text
grep "banana" sample.txt
What this command does:
grep
- The command to search for patterns"banana"
- The pattern we're searching for (the search term)sample.txt
- The file to search in
Output:
banana
Understanding the result:
- grep searched through every line in
sample.txt
- It found one line containing the word "banana"
- It printed that entire line to the terminal
- Lines without "banana" were ignored
π‘ How grep Works: grep reads files line by line. When it finds a line containing your pattern, it prints that entire line. If the pattern appears on multiple lines, all matching lines are printed.
What If Nothing Matches?
grep "orange" sample.txt
Output:
(no output - empty)
What happened:
- grep searched all lines in
sample.txt
- No lines contained the word "orange"
- grep produces no output when there are no matches
- The command exits silently (this is normal behavior)
π Case-Insensitive Searching
By default, grep is case-sensitive. The -i
option makes grep ignore case differences.
Using the -i Option
grep -i "Apple" sample.txt
What this command does:
grep
- The search command-i
- Option for case-insensitive matching- Tells grep to ignore uppercase/lowercase differences
"Apple"
- Pattern to search (note the capital 'A')sample.txt
- File to search
Output:
apple
Understanding the result:
- We searched for "Apple" (capital A)
- The file contains "apple" (lowercase a)
- The
-i
flag made grep treat "Apple" and "apple" as the same - grep found and printed the matching line
When to Use -i
Without -i (default) | With -i (case-insensitive) |
---|---|
grep "apple" finds "apple" | grep -i "apple" finds "apple", "Apple", "APPLE", "aPpLe" |
grep "ERROR" finds only "ERROR" | grep -i "ERROR" finds "error", "Error", "ERROR" |
Exact case match required | Any case combination matches |
β
Best Practice: Use -i
when searching log files for errors, as error messages might appear in different cases: "Error", "ERROR", or "error".
π’ Counting Matches
The -c
option counts how many lines contain the pattern instead of printing the lines.
Using the -c Option
grep -c "a" sample.txt
What this command does:
grep
- The search command-c
- Count option- Instead of printing matching lines, print the count
"a"
- Pattern to search (the letter 'a')sample.txt
- File to search
Output:
4
Understanding the result:
- grep counted lines containing the letter "a"
- It found 4 lines with the letter "a":
- apple (contains 'a')
- banana (contains 'a')
- date (contains 'a')
- grape (contains 'a')
- Lines without 'a' (cherry, elderberry, fig) were not counted
Important: Counting Lines, Not Occurrences
# This counts LINES containing "a", not how many times "a" appears
grep -c "a" sample.txt
# Output: 4
# Note: "banana" has 3 letter 'a's, but it's still just 1 line
Key distinction:
-c
counts lines that match, not total pattern occurrences- If a line has the pattern multiple times, it's still counted once
- "banana" contains three 'a's but is counted as 1 line
β Anchoring Patterns: Beginning of Line
The caret ^
symbol is a special character that matches the beginning of a line.
Lines Starting with Specific Pattern
grep "^a" sample.txt
What this command does:
grep
- Search command"^a"
- Pattern that matches 'a' at the start of a line^
- Anchor indicating "beginning of line"a
- The letter we're looking for at the start
sample.txt
- File to search
Output:
apple
Understanding the result:
- grep looked for lines beginning with the letter 'a'
- Only "apple" starts with 'a'
- "banana" contains 'a' but doesn't START with 'a', so it's not matched
- "date" contains 'a' but starts with 'd', so it's not matched
Visual Representation
Lines in file:
^apple β Matches (starts with 'a')
^banana β No match (starts with 'b')
^cherry β No match (starts with 'c')
^date β No match (starts with 'd')
π‘ Understanding ^ (Caret): The ^
is called an anchor in regular expressions. It doesn't match any characterβit matches a position (the start of a line). Think of it as saying "the pattern must be at the beginning."
π― Anchoring Patterns: End of Line
The dollar sign $
symbol matches the end of a line.
Lines Ending with Specific Pattern
grep "e$" sample.txt
What this command does:
grep
- Search command"e$"
- Pattern that matches 'e' at the end of a linee
- The letter we're looking for$
- Anchor indicating "end of line"
sample.txt
- File to search
Output:
apple
date
grape
Understanding the result:
- grep searched for lines ending with the letter 'e'
- Three lines end with 'e':
- apple β ends with 'e'
- date β ends with 'e'
- grape β ends with 'e'
- "banana" contains 'e' (elderberry contains 'e' in the middle) but doesn't END with 'e'
- "cherry" ends with 'y', not 'e'
Visual Representation
Lines in file:
apple$ β Matches (ends with 'e')
banana$ β No match (ends with 'a')
cherry$ β No match (ends with 'y')
date$ β Matches (ends with 'e')
elderberry$ β No match (ends with 'y')
fig$ β No match (ends with 'g')
grape$ β Matches (ends with 'e')
Combining Anchors
You can combine ^
and $
to match exact lines:
# Match only lines that are exactly "apple"
grep "^apple$" sample.txt
Output:
apple
This would match a line containing only "apple" and nothing else.
π¨ Character Classes
Character classes allow you to match any single character from a set of characters. They're enclosed in square brackets []
.
Matching Vowels Followed by 'p'
grep "[aeiou]p" sample.txt
What this command does:
grep
- Search command"[aeiou]p"
- Pattern with a character class[aeiou]
- Match any single vowel (a, e, i, o, or u)p
- Followed by the letter 'p'
sample.txt
- File to search
Breaking down the pattern:
[aeiou]
means "match exactly ONE of these letters: a, e, i, o, or u"- The
p
must come immediately after the vowel - So we're looking for: ap, ep, ip, op, or up
Output:
apple
grape
Understanding the result:
Line 1: apple
- Contains "apple"
- The 'a' (a vowel) followed by 'p' matches our pattern
[aeiou]p
- Line is printed
Line 2: grape
- Contains "grape"
- Looking backward: 'a' (a vowel) followed by 'p'
- Matches the pattern
[aeiou]p
- Line is printed
Why other lines don't match:
- banana - has vowels (a, a, a) but none followed by 'p'
- cherry - no letter 'p' at all
- date - no letter 'p' at all
- elderberry - has 'e', 'e', but no 'p' after them
- fig - no letter 'p' at all
More Character Class Examples
Pattern | Meaning | Example Matches |
---|---|---|
[abc] | Match a, b, or c | a, b, c |
[0-9] | Match any single digit | 0, 5, 9 |
[A-Z] | Match any uppercase letter | A, M, Z |
[a-z] | Match any lowercase letter | a, m, z |
[A-Za-z] | Match any letter (upper or lower) | A, m, Z, b |
[^aeiou] | Match anything EXCEPT vowels | b, c, d, f, g, etc. |
β οΈ Character Class vs Line Anchor: The ^
has different meanings:
- Inside brackets
[^aeiou]
means "NOT these characters" - Outside brackets
^apple
means "at the beginning of line"
π€ Extended Regular Expressions
Basic grep uses simple patterns. For more powerful patterns, we need Extended Regular Expressions using the -E
option.
The -E Option
The -E
option enables extended regex features like:
{n,m}
- Quantifiers (match n to m times)+
- One or more times?
- Zero or one time|
- Alternation (OR)
Finding Consecutive Vowels
Let's try to find words with two or more consecutive vowels (like "oo" in "book").
First, let's add a word with consecutive vowels to our file:
echo book >> sample.txt
What this command does:
echo book
- Output the word "book">>
- Append to file (unlike>
which overwrites)sample.txt
- The file to append to
Verify the addition:
cat sample.txt
Output:
apple
banana
cherry
date
elderberry
fig
grape
book
Now let's search for consecutive vowels:
grep -E "[aeiou]{2,}" sample.txt
What this command does:
grep
- Search command-E
- Enable Extended Regular Expressions- Required for
{2,}
quantifier to work
- Required for
"[aeiou]{2,}"
- Pattern for two or more consecutive vowels[aeiou]
- Match any vowel{2,}
- Match 2 or more times (consecutively)
sample.txt
- File to search
Output:
book
Understanding the result:
The pattern [aeiou]{2,}
means:
- Find any vowel
[aeiou]
- That appears at least 2 times in a row
{2,}
Checking each line:
- apple - a-p-p-l-e (vowels are separated by consonants) β
- banana - b-a-n-a-n-a (vowels are separated) β
- cherry - c-h-e-r-r-y (only one 'e', no consecutive vowels) β
- date - d-a-t-e (vowels are separated) β
- elderberry - e-l-d-e-r-b-e-r-r-y (vowels separated by consonants) β
- fig - f-i-g (only one vowel) β
- grape - g-r-a-p-e (vowels separated) β
- book - b-oo-k (two consecutive 'o's!) β
Understanding Quantifiers
Quantifier | Meaning | Example |
---|---|---|
{n} | Exactly n times | [0-9]{3} matches exactly 3 digits: 123, 456 |
{n,} | n or more times | a{2,} matches aa, aaa, aaaa, etc. |
{n,m} | Between n and m times | [0-9]{2,4} matches 12, 123, 1234 |
π Complex Pattern: Multiple Vowels
Let's try a more complex pattern that matches lines with multiple vowels (not necessarily consecutive).
grep -E '([aeiou].*){2,}' sample.txt
What this command does:
grep
- Search command-E
- Extended regex mode'([aeiou].*){2,}'
- Complex pattern[aeiou]
- Match any vowel.*
- Match any character (.
) zero or more times (*
)()
- Group the pattern{2,}
- The entire group must appear 2+ times
sample.txt
- File to search
Breaking down ([aeiou].*){2,}
:
[aeiou]
- Find a vowel.*
- Followed by any characters (including none)()
- Group these together{2,}
- This pattern must occur at least twice
This means: "Find a vowel followed by anything, and this pattern should appear at least 2 times."
Output:
apple
banana
date
elderberry
grape
Understanding why each line matches:
apple:
- First vowel: a (a-pple)
- Second vowel: e (appl-e)
- Has 2 vowels β
banana:
- First vowel: a (b-a-nana)
- Second vowel: a (ban-a-na)
- Third vowel: a (banan-a)
- Has 3 vowels β
date:
- First vowel: a (d-a-te)
- Second vowel: e (dat-e)
- Has 2 vowels β
elderberry:
- First vowel: e (e-lderberry)
- Second vowel: e (eld-e-rberry)
- Third vowel: e (elderb-e-rry)
- Has 3 vowels β
grape:
- First vowel: a (gr-a-pe)
- Second vowel: e (grap-e)
- Has 2 vowels β
Lines that DON'T match:
cherry: Has only 1 vowel ('e') β fig: Has only 1 vowel ('i') β book: Has only 1 distinct vowel (two 'o's count as the pattern appearing with that vowel, but 'o' only) β
π‘ Understanding .*: The dot .
matches any single character. The asterisk *
means "zero or more times." Together, .*
means "any number of any characters" - it's like a wildcard that can match anything or nothing.
π Real-World Example: Analyzing Log Files
Let's work with real system logs to see grep in action with practical data.
Creating a Log File Sample
journalctl > logs.txt
What this command does:
journalctl
- Command to query system journal logs- Displays system and service logs from systemd
>
- Redirect output to a filelogs.txt
- Save logs to this file
Check the log file size:
cat logs.txt | wc -l
What this pipeline does:
cat logs.txt
- Output the contents of logs.txt|
- Pipe the output to the next commandwc -l
- Count lineswc
= word count command-l
= count lines only
Sample Output:
3216
This means your log file contains 3,216 lines.
Previewing Log Content
cat logs.txt | head
Sample Output:
Oct 06 15:42:07 localhost kernel: Linux version 5.14.0-620.el9.x86_64 (mockbuild@x86-05.stream.rdu2.redhat.com) (gcc (GCC) 11.5.0 20240719 (Red Hat 11.5.0-11), GNU ld version 2.35.2-67.el9) #1 SMP PREEMPT_DYNAMIC Fri Sep 26 01:13:23 UTC 2025
Oct 06 15:42:07 localhost kernel: The list of certified hardware and cloud instances for Red Hat Enterprise Linux 9 can be viewed at the Red Hat Ecosystem Catalog, https://catalog.redhat.com.
Oct 06 15:42:07 localhost kernel: Command line: BOOT_IMAGE=(hd0,msdos1)/vmlinuz-5.14.0-620.el9.x86_64 root=/dev/mapper/cs_vbox-root ro resume=/dev/mapper/cs_vbox-swap rd.lvm.lv=cs_vbox/root rd.lvm.lv=cs_vbox/swap rhgb quiet crashkernel=1G-2G:192M,2G-64G:256M,64G-:512M
Oct 06 15:42:07 localhost kernel: BIOS-provided physical RAM map:
Oct 06 15:42:07 localhost kernel: BIOS-e820: [mem 0x0000000000000000-0x000000000009fbff] usable
Oct 06 15:42:07 localhost kernel: BIOS-e820: [mem 0x000000000009fc00-0x000000000009ffff] reserved
Oct 06 15:42:07 localhost kernel: BIOS-e820: [mem 0x00000000000f0000-0x00000000000fffff] reserved
Oct 06 15:42:07 localhost kernel: BIOS-e820: [mem 0x0000000000100000-0x00000000dffeffff] usable
Oct 06 15:42:07 localhost kernel: BIOS-e820: [mem 0x00000000dfff0000-0x00000000dfffffff] ACPI data
Oct 06 15:42:07 localhost kernel: BIOS-e820: [mem 0x00000000fec00000-0x00000000fec00fff] reserved
Understanding log format:
- Oct 06 15:42:07 - Timestamp (date and time)
- localhost - Hostname
- kernel: - Service/component that generated the log
- Rest of line - Log message content
Finding Errors in Logs
grep -i error logs.txt | head
What this pipeline does:
grep -i error logs.txt
- Search for "error" (case-insensitive)-i
makes it match "error", "Error", "ERROR", etc.
| head
- Show only first 10 results
Sample Output:
Oct 06 15:42:22 localhost kernel: vmwgfx 0000:00:02.0: [drm] *ERROR* vmwgfx seems to be running on an unsupported hypervisor.
Oct 06 15:42:22 localhost kernel: vmwgfx 0000:00:02.0: [drm] *ERROR* This configuration is likely broken.
Oct 06 15:42:22 localhost kernel: vmwgfx 0000:00:02.0: [drm] *ERROR* Please switch to a supported graphics device to avoid problems.
Oct 06 15:43:04 localhost alsactl[947]: alsa-lib main.c:1554:(snd_use_case_mgr_open) error: failed to import hw:0 use case configuration -2
Oct 06 15:43:13 vbox containerd[1896]: time="2025-10-06T15:43:13.567067537+05:00" level=info msg="skip loading plugin" error="aufs is not supported"
Understanding the results:
- All lines contain the word "error" somewhere
- Different services reported errors: kernel, alsactl, containerd
- Each line shows when the error occurred
- The
-i
flag caught "ERROR" and "error" (different cases)
Viewing Recent Errors
grep -i error logs.txt | tail
What this does:
tail
shows the LAST 10 lines instead of first- Useful for seeing most recent errors
Sample Output:
Oct 06 15:49:54 vbox gnome-shell[11143]: Received error from D-Bus search provider firefox.desktop: Gio.DBusError: GDBus.Error:org.freedesktop.DBus.Error.ServiceUnknown: The name is not activatable
Oct 06 15:49:54 vbox gnome-shell[11143]: Received error from D-Bus search provider firefox.desktop: Gio.DBusError: GDBus.Error:org.freedesktop.DBus.Error.ServiceUnknown: The name is not activatable
Oct 06 15:51:01 vbox google-chrome.desktop[14490]: [14477:14477:1006/155101.817571:ERROR:services/on_device_model/public/cpp/service_client.cc:36] Unexpected on_device_model service disconnect: Error loading backend.
Oct 06 15:51:04 vbox google-chrome.desktop[14490]: [14477:14522:1006/155104.678084:ERROR:google_apis/gcm/engine/registration_request.cc:291] Registration response error message: DEPRECATED_ENDPOINT
Oct 06 15:54:52 vbox google-chrome.desktop[14490]: [14477:14522:1006/155452.845805:ERROR:google_apis/gcm/engine/registration_request.cc:291] Registration response error message: DEPRECATED_ENDPOINT
Finding Log Lines with Date Pattern
grep -E "^[A-Za-z]{3} [0-9]{2}" logs.txt | head
What this complex pattern does:
grep -E
- Extended regex mode"^[A-Za-z]{3} [0-9]{2}"
- Pattern matching log timestamp format^
- Beginning of line[A-Za-z]{3}
- Exactly 3 letters (month abbreviation like "Oct")[0-9]{2}
- Exactly 2 digits (day of month like "06")
logs.txt
- File to search| head
- Show first 10 matches
Sample Output:
Oct 06 15:42:07 localhost kernel: Linux version 5.14.0-620.el9.x86_64 ...
Oct 06 15:42:07 localhost kernel: The list of certified hardware ...
Oct 06 15:42:07 localhost kernel: Command line: BOOT_IMAGE ...
Oct 06 15:42:07 localhost kernel: BIOS-provided physical RAM map:
Oct 06 15:42:07 localhost kernel: BIOS-e820: [mem ...] usable
Why this pattern is useful:
- Filters out malformed log entries
- Ensures you're looking at properly formatted logs
- Common pattern for analyzing system logs
- Helps identify log structure issues
π― Best Practices
β General grep Best Practices
-
Use quotes around patterns: Always quote your search patterns
- β
grep "error" file.txt
- β
grep error file.txt
(can break with special characters)
- β
-
Use -i for flexible searches: When case doesn't matter
- β
grep -i "error"
catches Error, ERROR, error - Especially useful for log file searches
- β
-
Combine with other commands: grep works great in pipelines
command | grep pattern
- Filter command outputgrep pattern file | wc -l
- Count matches
-
Use -E for complex patterns: When you need regex features
- β
grep -E "[0-9]{3,}"
- Requires -E - β
grep -E "error|warning|critical"
- Multiple patterns
- β
-
Test patterns incrementally: Build complex patterns step by step
- Start simple:
grep "error"
- Add features:
grep -i "error"
- Add complexity:
grep -iE "error|warning"
- Start simple:
β Performance Tips
-
Limit output when exploring: Use
head
ortail
with large filesgrep pattern large_file.txt | head -20
- Prevents overwhelming your terminal
-
Use -c for counting: More efficient than piping to
wc -l
- β
grep -c "pattern" file.txt
- β
grep "pattern" file.txt | wc -l
(works but creates extra process)
- β
-
Search specific files: Don't grep everything if you know the file
- β
grep "error" /var/log/syslog
- β
grep "error" /var/log/*
(when you only need one file)
- β
β Common Use Cases
Finding processes:
ps aux | grep nginx
Searching code:
grep -r "function_name" /path/to/code
Excluding grep itself from results:
ps aux | grep nginx | grep -v grep
Searching multiple files:
grep "TODO" *.js
π grep Command Cheat Sheet
Basic Options
# Basic search
grep "pattern" filename
# Case-insensitive search
grep -i "pattern" filename
# Count matching lines
grep -c "pattern" filename
# Show line numbers
grep -n "pattern" filename
# Invert match (show NON-matching lines)
grep -v "pattern" filename
# Recursive search in directories
grep -r "pattern" /path/to/directory
# Show only filenames with matches
grep -l "pattern" *.txt
# Show filenames without matches
grep -L "pattern" *.txt
Anchor Patterns
# Lines starting with pattern
grep "^pattern" filename
# Lines ending with pattern
grep "pattern$" filename
# Lines containing ONLY the pattern (exact match)
grep "^pattern$" filename
# Empty lines
grep "^$" filename
Character Classes
# Any vowel
grep "[aeiou]" filename
# Any digit
grep "[0-9]" filename
# Any lowercase letter
grep "[a-z]" filename
# Any uppercase letter
grep "[A-Z]" filename
# Any letter (upper or lowercase)
grep "[A-Za-z]" filename
# Anything EXCEPT vowels
grep "[^aeiou]" filename
Extended Regular Expressions (-E)
# One or more digits
grep -E "[0-9]+" filename
# Optional character
grep -E "colou?r" filename # Matches color or colour
# Alternation (OR)
grep -E "error|warning|critical" filename
# Exactly 3 digits
grep -E "[0-9]{3}" filename
# 2 to 4 digits
grep -E "[0-9]{2,4}" filename
# 3 or more letters
grep -E "[a-z]{3,}" filename
# IP address pattern
grep -E "[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}" filename
Combining with Other Commands
# Find in command output
ps aux | grep nginx
# Count errors in log
grep -c "error" /var/log/syslog
# Find and exclude grep itself
ps aux | grep nginx | grep -v grep
# Multiple patterns with OR
grep -E "error|warning" logfile.txt
# Case-insensitive with line numbers
grep -in "error" logfile.txt
# Show 3 lines before and after match
grep -C 3 "error" logfile.txt
# Show 2 lines before match
grep -B 2 "error" logfile.txt
# Show 2 lines after match
grep -A 2 "error" logfile.txt
Real-World Examples
# Find all error logs from today
grep "$(date +%b\ %d)" /var/log/syslog | grep -i error
# Find email addresses in a file
grep -E "[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}" filename
# Find URLs in a file
grep -E "https?://[a-zA-Z0-9./?=_%:-]*" filename
# Find lines with phone numbers (US format)
grep -E "\([0-9]{3}\) [0-9]{3}-[0-9]{4}" filename
# Find lines that are comments in code (starting with #)
grep "^#" script.sh
# Find TODO comments in code
grep -rn "TODO" /path/to/code
Useful Combinations
# Search multiple files
grep "pattern" file1.txt file2.txt file3.txt
# Search all text files
grep "pattern" *.txt
# Recursive search excluding certain directories
grep -r "pattern" /path --exclude-dir={node_modules,dist,build}
# Highlight matches in color
grep --color=auto "pattern" filename
# Count total occurrences across multiple files
grep -roh "pattern" /path | wc -l
π What's Next?
π Continue Learning
Now that you've mastered grep, explore these related topics:
- sed: Stream editor for text transformation
- awk: Powerful text processing and data extraction
- Regular expressions: Deep dive into advanced pattern matching
- find: Locating files based on various criteria
- Piping and redirection: Combining commands effectively
π οΈ Practice Projects
Apply your grep skills:
- Analyze web server access logs for error codes
- Search source code for specific functions or TODO comments
- Monitor system logs for security issues
- Extract specific data from CSV files
- Build log analysis scripts combining grep, awk, and sort
π Congratulations! You've mastered the grep command from basic text searches to advanced regular expression pattern matching. You can now efficiently search, filter, and analyze text files like a Linux pro!
π¬ Discussion
I'd love to hear about your experience with grep:
- What text searching challenges have you encountered?
- Have you discovered any useful grep patterns for your work?
- What other text processing topics would you like to learn about?
- Do you have any grep tips to share with other beginners?
Connect with me: