Why the Y Command?

Learning SED

Chapter 9 — The y Command and Other Commands

Completing the Command Set

The previous chapters covered SED's core commands — s, d, p, q, a, i, c, the hold space commands, the multi-line trio, and branching. This chapter covers the remaining commands that round out the full SED toolkit. Some are used daily; others are specialist tools for specific tasks.

y/src/dst/
Transliterate
Map characters one-for-one — every character in src is replaced by the corresponding character in dst. Works on the entire pattern space, always global.
POSIX standard
r file
Read file
After printing the current line, read the contents of file and insert them into the output stream.
POSIX standard
R file
Read one line from file
Like r but reads only the next unread line from file, advancing a file pointer with each call.
GNU sed only
w file
Write to file
Write the pattern space to file immediately. The file is created (or truncated) at script start, then appended to during processing.
POSIX standard
W file
Write first line to file
Like w but writes only the first line of the pattern space — useful after multi-line accumulation with N.
GNU sed only
e
Execute
Execute the pattern space as a shell command and replace it with the command's output. Or (with argument) execute a command without touching pattern space.
GNU sed only
z
Zap (clear)
Clear the pattern space — make it empty without starting a new cycle. Unlike d, the rest of the script continues to run.
GNU sed only
=
Print line number
Print the current input line number to stdout followed by a newline. Introduced in Chapter 4; included here for the complete command reference.
POSIX standard

The y Command — Transliterate

The y command performs character-by-character translation across the entire pattern space. It works like the standalone tr utility but is integrated into SED so it can be combined with addresses and other commands in a single pass.

The syntax mirrors s but uses source and destination character lists instead of a pattern and replacement:

y/source-chars/dest-chars/

Every character in the pattern space that appears in source-chars is replaced by the character at the same position in dest-chars. The two lists must be the same length. Like s, the delimiter can be changed to any character.

y/abc/ABC/ — mapping aA bB cC all other chars unchanged

Case conversion

# Convert lowercase a-z to uppercase A-Z
sed 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/' file.txt

# Convert uppercase to lowercase
sed 'y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/' file.txt

# GNU sed shorthand using character classes in s// is often cleaner for case
sed 's/.*/\U&/' file.txt   # uppercase (GNU sed \U extension)
sed 's/.*/\L&/' file.txt   # lowercase (GNU sed \L extension)

ROT13 encoding

# Classic ROT13 — rotate letters by 13 positions (encode and decode are identical)
sed 'y/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz/NOPQRSTUVWXYZABCDEFGHIJKLMnopqrstuvwxyzabcdefghijklm/' file.txt

Transliterating digits and special characters

# Replace digits 0-9 with their word equivalents using a proxy
# (y can only map single chars — for multi-char replacements use s//)

# Swap pairs of characters (simple cipher)
sed 'y/aeiouAEIOU/eioauEIOAU/' file.txt

# Replace path separators — Windows to Unix
sed 'y/\\/\//' winpaths.txt
# Backslash → forward slash (each needs escaping)

# Translate specific punctuation
sed 'y/;:,/|||/' data.txt
# Replace semicolons, colons, and commas all with pipe characters

y vs s — knowing which to use

y/src/dst/ s/pattern/replacement/
Unit of operation Individual characters Patterns (strings, regex)
Scope Always global — entire pattern space First match only (unless g flag)
Replacement length One char → one char (lists must match in length) Any length → any length
Regex support No — source is a literal character list Yes — full regex in pattern
Special sequences Only \n, \\, \/ &, \1\9, \u, \L, etc.
Best for Character-set mapping, encoding, case conversion Everything else
y does not support character ranges like a-z. Unlike tr, you must list every character explicitly: y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/. The - character is treated as a literal hyphen in y, not a range specifier.

The r and R Commands — Reading Files

The r command reads the entire contents of a named file and inserts them into the output stream after the addressed line. It does not affect the pattern space — the file content is sent directly to output after the current line's normal print completes. If the file does not exist, r silently does nothing (no error).

r filename       # note: there must be exactly one space between r and the filename

Inserting a file after a pattern match

# Insert a standard footer after the last line of every file
sed '$r footer.txt' document.txt

# Insert a licence header after line 1 of a script
# (use a after line 1 for before — r always appends after)
sed '1r licence_header.txt' script.sh

# Insert a file after every line matching a pattern
sed '/^## Summary/r summary_boilerplate.txt' report.md

Template substitution — the killer use case for r

The most powerful use of r is template filling: replace a placeholder line with the contents of another file. Combine r (to insert the file after the placeholder) with d (to delete the placeholder itself):

# template.html contains the line: ##CONTENT_PLACEHOLDER##
# Replace it with the contents of content.html
sed '/##CONTENT_PLACEHOLDER##/ {
  r content.html
  d
}' template.html

# Same result, more compact:
sed '/PLACEHOLDER/ { r insert.txt
d }' template.txt
The order matters: r must come before d in the block. Since r schedules output after the current line's print — and d suppresses that print — this combination works correctly: d removes the placeholder line from output, while the scheduled file content still appears at that position. If you put d first, the r would never execute.

The R command — reading one line at a time

R (GNU sed only) reads a single line from a file each time it fires, advancing an internal pointer. This lets you interleave lines from two files:

# Interleave every line of file.txt with lines from extra.txt
sed 'R extra.txt' file.txt
# Output: line1 of file.txt
#         line1 of extra.txt
#         line2 of file.txt
#         line2 of extra.txt  ...

# Substitute a placeholder with one line from a data file each time it appears
sed '/TEMPLATE_LINE/ {
  R data.txt
  d
}' template.txt

The w and W Commands — Writing Files

The w command writes the pattern space to a named file. SED creates (or truncates) the output file when the script starts, then appends to it each time w fires during processing. The file is ready to use as soon as SED finishes.

w filename       # one space between w and filename

Splitting a file by content

# Route error lines to errors.log and everything else to normal.log
sed -n '/ERROR/  w errors.log
         /ERROR/! w normal.log' app.log

# Split a CSV into separate files by first field value
# (limited — sed can only write to fixed filenames, not dynamic ones)
sed -n '/^alice,/ w alice.csv
/^bob,/   w bob.csv
/^carol,/ w carol.csv' users.csv

Writing a subset of lines to a file

# Extract all lines containing IP addresses to a separate file
sed -n '/[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}/ w ips.txt' logfile.txt

# Write changed lines to a log while also applying changes in-place
sed -i.bak 's/old_api_url/new_api_url/w changes.log' config.js
# -i.bak edits in place with backup; w flag on s// logs changed lines

Using w as a side-effect alongside normal output

# Print all lines normally, but ALSO write matching lines to a separate file
sed '/ERROR/ w error_summary.txt' app.log
# All lines go to stdout as normal; ERROR lines are additionally written to the file

The W command — writing only the first line

# After N has joined lines, write only the first line of the pair to a file
sed -n '$!N; /^key:/ W keys_only.txt; P; D' data.txt
File creation behaviour: SED creates (or truncates to zero) all files named in w commands at the very start of execution — even before any input is read. This means if no lines match, the output file still exists, just empty. This is useful to know when checking if a w command produced any output: test file size (-s) rather than file existence.

The e Command — Execute

The e command (GNU sed only) has two distinct forms with different behaviours:

Form Syntax What it does
As a standalone command e command Execute command in a shell; insert its output into the output stream before the pattern space is printed
As a flag on s s/pat/rep/e After substitution, execute the resulting pattern space as a shell command and replace the pattern space with the command's output

Using e as a standalone command

# Insert the current date before every line of the file
sed 'e date "+%Y-%m-%d"' file.txt

# Run a command and inject its output before matching lines
sed '/^hostname:/ e hostname' server.conf
# Inserts the actual hostname before each "hostname:" line

Using the e flag on s

# For each filename in a list, replace it with its actual file size
sed 's/.*/stat --format="%s bytes: &" &/e' filelist.txt
# s builds a stat command from each filename
# e flag executes that command and replaces the line with its output

# Evaluate each line as a shell arithmetic expression
sed 's/.*/echo $((&))/e' expressions.txt
# Input: "2 + 2"
# s wraps it: "echo $((2 + 2))"
# e executes it → output: "4"
The e command is powerful and dangerous. It executes arbitrary shell commands using whatever the pattern space contains at that moment. Never use e on untrusted input — a crafted input line could execute any command on your system. Reserve it for scripts where you fully control the input data.

The z Command — Zap the Pattern Space

The z command (GNU sed only) clears the pattern space — sets it to an empty string — without starting a new cycle. This is the key difference from d:

d — delete z — zap
Pattern space after Discarded — cycle ends immediately Empty string — script continues
Remaining script commands Skipped entirely Still execute (on empty pattern space)
Default print Suppressed Prints a blank line (empty pattern space)
Use when You want to remove the line completely You want to clear the content but still run subsequent commands
# Clear the pattern space but append the hold space onto the now-empty line
sed '/HEADER/ { z; G }' file.txt
# z empties the pattern space; G appends the hold space
# Net result: the HEADER line's content is replaced with whatever is in hold space
# (different from c because z + G lets you build the replacement dynamically)

# Use z to reset accumulated content mid-script
sed -n '/START/ { H }
/END/   { H; g; /important/p; z; h }
/START/! { /END/! H }' file.txt
# z resets the pattern space after printing a block
# h then saves the now-empty pattern space back to hold, resetting the accumulator

Practical: z as a conditional line suppressor

# Clear lines that fail a validation check (output blank line as placeholder)
sed '/^[0-9]\+$/ !z' numbers.txt
# Lines that are NOT pure numbers: z clears them (outputs blank line)
# Lines that ARE pure numbers: printed unchanged

# Then pipe to remove the blank lines if you don't want them:
sed '/^[0-9]\+$/ !z' numbers.txt | sed '/^$/d'
# (equivalent to: sed -n '/^[0-9]\+$/p' numbers.txt)

The = Command — Print Line Number

Already introduced in Chapter 4, = prints the current line number to stdout followed by a newline. It is included here for the complete command reference.

# Count lines in a file (print only the last line number)
sed -n '$=' file.txt

# Print line numbers alongside matching lines (grep -n equivalent)
sed -n '/ERROR/ { =; p }' logfile.txt

# Add line numbers to every line's output (cat -n equivalent)
sed '=' file.txt | paste - -
# = prints number on its own line; paste merges pairs of lines

Combining These Commands — Practical Recipes

Build a simple template engine

# template.conf:
#   ServerName ##HOSTNAME##
#   ##SSL_CONFIG##
#   DocumentRoot /var/www/##SITENAME##

sed -e 's/##HOSTNAME##/www.example.com/g' \
    -e 's/##SITENAME##/mysite/g' \
    -e '/##SSL_CONFIG##/ { r ssl_block.conf
                         d }' \
    template.conf

Audit log: apply changes and record what changed

# Edit in place, write a change log simultaneously
sed -i.bak 's/password=.*/password=REDACTED/w redacted.log' config.ini
# -i.bak: edit in place with backup
# w flag: write lines where substitution succeeded to redacted.log

ROT13 encode only comment lines in a script

sed '/^#/ y/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz/NOPQRSTUVWXYZABCDEFGHIJKLMnopqrstuvwxyzabcdefghijklm/' script.sh
# y only runs on lines matching /^#/ — code lines are untouched

Split output into matched and unmatched files

# Write every line to one of two output files, suppress stdout
sed -n '/^[0-9]/ w numeric.txt
       /^[^0-9]/ w alpha.txt' mixed.txt

Interleave a file with generated line numbers

# Number each line with = and merge onto same line as content
sed '=' file.txt | sed 'N; s/\n/\t/'
# First sed: = prints number on its own line before each content line
# Second sed: N joins pairs; s replaces the joining newline with a tab

Evaluate shell expressions embedded in a config file

# A config file contains lines like: path=$(pwd)/subdir
# Evaluate only those lines
sed '/\$(/s/.*/echo "&"/e' config.txt
# s wraps matched lines in echo "..."
# e executes: echo evaluates the $() expansion
# WARNING: only use on trusted files

Generate a numbered HTML list from plain text

# Wrap each line in <li> tags and add ol wrappers
sed -e '1i\<ol>' \
    -e 's/.*\/  <li>&<\/li>/' \
    -e '$a\<\/ol>' \
    items.txt

Complete SED Command Reference

Now that all commands have been covered, here is the full command set in one place:

Command Description POSIX?
s/pat/rep/flagsSubstituteYes
dDelete pattern space; start next cycleYes
DDelete first line of pattern space; restart scriptYes
pPrint entire pattern spaceYes
PPrint first line of pattern spaceYes
q [N]Print pattern space (unless -n), then quit with exit code NPartial (exit code: GNU)
Q [N]Quit without printing, with optional exit code NGNU only
a\textAppend text after current lineYes
i\textInsert text before current lineYes
c\textReplace current line (or range) with textYes
nPrint pattern space; read next line into itYes
NAppend next line to pattern spaceYes
hCopy pattern space to hold spaceYes
HAppend pattern space to hold spaceYes
gCopy hold space to pattern spaceYes
GAppend hold space to pattern spaceYes
xExchange pattern space and hold spaceYes
y/src/dst/Transliterate charactersYes
=Print current line numberYes
r fileRead file, insert after current lineYes
R fileRead one line from file, insert after current lineGNU only
w fileWrite pattern space to fileYes
W fileWrite first line of pattern space to fileGNU only
e [cmd]Execute shell command; replace pattern space with outputGNU only
zClear (zap) the pattern spaceGNU only
:labelDeclare a branch labelYes
b [label]Branch to label (or end of script)Yes
t [label]Branch to label if substitution succeededYes
T [label]Branch to label if no substitution succeededGNU only

Quick Reference — Chapter 9

Transliterate — y

y/src/dst/ Replace each char in src with the corresponding char in dst (always global)
y/a-z/A-Z/ Does NOT work — y has no range support; list all chars explicitly

Read and Write Files

r file Insert entire file after current line (POSIX)
R file Insert next unread line of file after current line (GNU)
w file Write pattern space to file (appending; file created at script start)
W file Write first line of pattern space to file (GNU)
/pat/{r f; d} Replace placeholder line with file contents (template pattern)

Execute and Zap

e Execute pattern space as shell command; replace with output (GNU)
e command Execute command; insert output before current line (GNU)
s/pat/rep/e Substitute then execute result as shell command (GNU)
z Clear pattern space without ending the cycle (GNU)
What is coming next: Chapter 10 goes deep into regular expressions as used specifically in SED — Basic Regular Expressions (BRE) vs Extended Regular Expressions (ERE), character classes, anchors, quantifiers, word boundaries, and the SED-specific quirks and gotchas that catch even experienced users out.