Capture groups and in-place editing

grep, sed & awk · Practical · 12 min

Most real sed jobs rearrange parts of a line and then save the result back into the file.

Parentheses in the pattern capture what they match; \1 to \9 in the replacement insert those captures. In BRE the parentheses must be escaped, so use sed -E (ERE) and write plain ( ):

sed -E 's/([a-z]+)@([a-z.]+)/\2: \1/'

GNU sed also offers case conversion in the replacement: \U upper-cases what follows, \L lower-cases, \E stops; \u and \l affect only the next character.

To change a file in place use -i. GNU sed writes a temporary file and renames it over the original. Give a suffix to keep a backup: sed -i.bak 's/a/b/' file leaves file.bak behind.

Portability matters here. On macOS and the BSDs -i requires a suffix argument, so sed -i 's/a/b/' f fails there; the empty suffix is written sed -i '' .... The form -i.bak (no space) works on both GNU and BSD sed, so prefer it in shared scripts, then delete the backup if you don't need it.

Before running -i on many files, preview the change without -i (or with diff) first. Because -i replaces the file, it also breaks hard links and resets ownership in some cases, which matters for files edited as root.

Example

echo 'alice@example.com' | sed -E 's/([a-z]+)@([a-z.]+)/\2: \1/'
# example.com: alice

echo '2026-10-17' | sed -E 's/([0-9]{4})-([0-9]{2})-([0-9]{2})/\3.\2.\1/'
# 17.10.2026

echo 'hello world' | sed -E 's/(\w+)/\u\1/g'     # Hello World (GNU)

printf 'debug=true\nlevel=3\n' > app.conf
sed -i.bak 's/^debug=true/debug=false/' app.conf   # works with GNU and BSD sed
cat app.conf          # debug=false / level=3
cat app.conf.bak      # debug=true  / level=3
diff app.conf.bak app.conf    # shows the one changed line

Remember

  • sed -E lets you write ( ) for groups; \1..\9 reuse them in the replacement
  • -i edits in place; -i.bak keeps a backup and is portable to BSD/macOS
  • Preview without -i before rewriting many files

Watch out for

  • Running sed -i 's/x/y/' file on macOS, where the s/// expression is taken as the backup suffix and the command fails.

Check yourself

  1. What does echo 'b a' | sed -E 's/(.) (.)/\2 \1/' print?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: www.gnu.org/software/sed/manual/sed.html#Back_002dreferences-and-Subexpressions

Builds on

Unlocks

Nothing builds on this yet.