A pocket reference for replacing sed with perl. The big win: perl -i
behaves identically on macOS and Linux, unlike sed -i (BSD vs GNU).
| Flag | Meaning |
|---|---|
-e |
"the program is this string" (the code lives on the command line) |
-p |
loop over every line, run the code, then print the line (like sed) |
-n |
loop over every line, run the code, don't auto-print (you print) |
-i |
edit file(s) in place |
-i.bak |
edit in place, but save the original as file.bak first |
-l |
auto-chomp input newlines and re-add them on print (tidy line handling) |
-a |
autosplit each line into @F on whitespace (awk-like) |
-F |
set the autosplit delimiter, e.g. -F: or -F',' |
Mnemonic: -p prints for you, -n does not.
| Task | sed | perl |
|---|---|---|
| Substitute (stream to stdout) | sed 's/old/new/' |
perl -pe 's/old/new/' |
| Substitute, all on line | sed 's/old/new/g' |
perl -pe 's/old/new/g' |
| Substitute in place | sed -i 's/a/b/' f (GNU) |
perl -i -pe 's/a/b/' f |
| In place, keep backup | sed -i.bak 's/a/b/' f |
perl -i.bak -pe 's/a/b/' f |
| Case-insensitive | sed 's/old/new/gI' |
perl -pe 's/old/new/gi' |
| Print only matching lines | sed -n '/foo/p' |
perl -ne 'print if /foo/' |
| Delete matching lines | sed '/foo/d' |
perl -ne 'print unless /foo/' |
| Print a specific line (#5) | sed -n '5p' |
perl -ne 'print if $. == 5' |
| Print a line range (5–10) | sed -n '5,10p' |
perl -ne 'print if 5..10' |
The macOS gotcha: BSD
sed -irequires an argument (sed -i '' 's/a/b/' f), GNUsed -iforbids one.perl -ijust works the same everywhere.
# Pick a different delimiter when the pattern has slashes — no escaping needed
perl -pe 's{/usr/local}{/opt}g' # braces
perl -pe 's|/usr/local|/opt|g' # pipes
# Capture groups + backreferences ($1, $2, ...)
perl -pe 's/(\w+)\s+(\w+)/$2 $1/' # swap two words
# Anchors
perl -pe 's/^/ /' # indent every line 4 spaces
perl -pe 's/\s+$//' # strip trailing whitespace
# Case-changing in the replacement
perl -pe 's/(\w+)/\U$1/' # uppercase (\L lower, \u/\l one char)
# Only substitute on lines matching a condition
perl -pe 's/foo/bar/ if /^config/' # only on lines starting with "config"# One file
perl -i -pe 's/old/new/g' file.txt
# Many files at once
perl -i -pe 's/2025/2026/g' *.md
# Safest habit: keep a backup until you trust the result
perl -i.bak -pe 's/old/new/g' file.txtWant to preview first? Drop the -i — it streams the result to your terminal
without touching the file. Confirm, then re-run with -i.
# Print the 2nd whitespace-separated field of each line
perl -ane 'print "$F[1]\n"'
# Sum the first column of numbers
perl -ane '$sum += $F[0]; END { print "$sum\n" }'
# CSV: print column 3 (naive split — fine for simple, comma-free data)
perl -F, -ane 'print "$F[2]\n"'
# Grep with a regex, but also rewrite as you go
perl -ne 'print "$1\n" if /id=(\d+)/'# Slurp the entire file as one string (-0777), then operate across newlines
perl -0777 -pe 's/\n{3,}/\n\n/g' file # collapse 3+ blank lines into one
# This is something plain sed can't do cleanly — a real reason to switch.- Quote with single quotes in the shell so
$1,@F, etc. aren't eaten by bash. @inside a double-quoted Perl context looks like an array — escape it:\@.$means "end of line" in a regex but "variable" elsewhere; mind the context.-palready prints; adding your ownprintwill duplicate lines. Use-nwhen you print manually.- Test destructive edits on a copy, or use
-i.bak, before running on*.
perl -pe 's/old/new/g' # find & replace, preview to stdout
perl -i -pe 's/old/new/g' file # ...same, in place
perl -i.bak -pe 's/old/new/g' f # ...in place with a safety backup
perl -ne 'print if /pattern/' # grep-ish
perl -ne 'print unless /pattern/' # delete matching lines