Cao Yi

Find and Replace a String in a Directory (Recursive)

Index

You’re not just replacing text—you’re performing bulk surgery on your codebase. Do it carefully.


The Problem

You want to:

  1. Find all files containing a string
  2. Replace that string across many files
  3. Do it safely (without breaking things)

This is a common task in real projects: renaming variables, updating API endpoints, or fixing repeated mistakes.


Mental Model (How This Actually Works)

Think of the pipeline like this:

[grep] → [xargs] → [sed/perl]
  │         │           │
  │         │           └── Modify file content
  │         └────────────── Pass file list safely
  └──────────────────────── Find matching files

Or more concretely:

Search → Select files → Apply transformation

Each tool does one job—and does it well.


Step 1: Find Files Containing a String

grep -rl "abcabc" .

What this does

Important detail


Step 2: Replace the String in All Matching Files

grep -rlZ --exclude-dir=.git "abcabc" . \
| xargs -0 perl -pi -e 's/abcabc/cdecde/g'

Why this version is solid


Breaking It Down

1. grep -rlZ

Why this matters:

Normal output:
file one.txt
file two.txt

With -Z:
file one.txt\0file two.txt\0

This avoids breaking on spaces or weird filenames.


2. xargs -0

Without this, filenames like:

my file.txt

would break your command.


3. perl -pi -e

Replacement:

s/abcabc/cdecde/g

Why Not Just Use sed?

You can, but there are pitfalls.

Linux (GNU sed)

sed -i 's/abcabc/cdecde/g'

macOS (BSD sed)

sed -i '' 's/abcabc/cdecde/g'

That tiny difference ('') breaks scripts across platforms.

👉 This is why perl is often the safer default.


Safety First (Don’t Skip This)

1. Preview before changing anything

Dry-run / preview (before actual replacement):

# just list affected files
grep -rl "abcabc" .

Or inspect matches:

# show actual matching lines
grep -r "abcabc" .

2. Create backups

grep -rlZ "abcabc" . \
| xargs -0 perl -pi.bak -e 's/abcabc/cdecde/g'

This creates a .bak backup of every modified file.


3. Exclude dangerous directories

Always consider excluding:

Example:

grep -rlZ \
  --exclude-dir=.git \
  --exclude-dir=node_modules \
  "abcabc" .

Common Variations

Case-insensitive replace

grep -rilZ "abcabc" . \
| xargs -0 perl -pi -e 's/abcabc/cdecde/gi'

Replace whole words only

perl -pi -e 's/\babcabc\b/cdecde/g'

(\b works reliably in Perl, not always in sed)


Limit to specific file types

grep -rlZ --include="*.txt" --include="*.md" "abcabc" .

A Practical Rule of Thumb

If you remember nothing else, remember this:

Search with grep, pass safely with xargs -0, modify with perl.

That combination is:


grep -rlZ --exclude-dir=.git "abcabc" . \
| xargs -0 perl -pi -e 's/abcabc/cdecde/g'

Closing Thought

Bulk replacement is powerful—but also dangerous.

A careless command can:

So treat it like any other engineering change:

Do that, and this becomes one of the most useful tools in your workflow.