sed Command in Linux: with Real Examples

If you’ve spent any time on the Linux command line, you’ve probably seen the sed command used in a one-liner and thought “I should learn that properly.” Most people pick it up piecemeal, learning just enough to substitute text and move on. That’s leaving a lot on the table.

sed stands for stream editor. It reads input line by line, applies your instructions, and writes the result to standard output. No interactive interface, no temp files needed. It’s fast, scriptable, and available on every Linux system you’ll ever touch.

This guide covers the sed commands and patterns I actually use day to day, from simple substitutions to multi-expression edits and in-place file changes. If you work with config files, logs, or text pipelines, this is the reference you’ll keep coming back to.

In This Article

How sed Processes Text

Linux terminal showing "history | grep sed" output with real sed commands

Before diving into commands, it helps to understand what sed is actually doing under the hood. When sed reads a file, it works with two internal buffers:

  • Pattern space: the active buffer where sed places the current line and applies your commands. It gets cleared after each line is processed (unless you tell it otherwise).
  • Hold space: a secondary buffer for temporary storage. Data can be moved between the pattern space and hold space using h, H, g, G, and x commands. It persists across lines.

For most day-to-day work, you’ll never touch the hold space directly. But knowing it exists explains how sed can handle multi-line operations and why certain advanced recipes work the way they do. The GNU sed manual covers the execution cycle in full detail.

Basic Syntax

The general form is:

sed [options] 'command' file

Or piped from another command:

some-command | sed 'command'

You can also read commands from a script file using -f, which is useful when your edits get complex enough to be worth saving.

Substitution: The Command You’ll Use Most

The s command is the workhorse of sed. The syntax is:

sed 's/pattern/replacement/' file

By default it replaces only the first match on each line. To replace all matches on every line, add the g flag:

sed 's/foo/bar/g' file.txt

Case-insensitive matching uses the I flag (GNU sed):

sed 's/error/WARNING/gI' app.log

You can also target only a specific occurrence. This replaces only the second match on each line:

sed 's/foo/bar/2' file.txt

Note: The delimiter doesn’t have to be /. If your pattern contains slashes, use any other character. This is handy for file paths:

sed 's|/old/path|/new/path|g' config.txt

Editing Files In-Place

By default sed writes to stdout and leaves the original file untouched. To edit the file directly, use -i:

sed -i 's/ServerName old.example.com/ServerName new.example.com/' /etc/apache2/apache2.conf

Always back up first when editing config files. The -i flag accepts an optional suffix to create a backup automatically:

sed -i.bak 's/listen 80/listen 8080/' /etc/nginx/nginx.conf

That leaves nginx.conf.bak as your safety net. I use this constantly when scripting config changes across multiple servers.

Restricting Changes to Specific Lines

You can tell sed to operate only on certain lines by adding an address before the command.

By line number:

sed '3s/foo/bar/' file.txt

A range of lines:

sed '5,10s/foo/bar/g' file.txt

From a line to the end of file:

sed '5,$s/foo/bar/g' file.txt

Only lines matching a pattern:

sed '/^#/s/old/new/g' file.txt

That last one is useful. It restricts substitution to lines starting with #, so you can safely modify comments without touching live config values.

You can also negate an address with !. This applies the substitution to every line that does NOT start with #:

sed '/^#/!s/old/new/g' file.txt

Deleting Lines

The d command deletes matching lines from the output.

Delete blank lines:

sed '/^$/d' file.txt

Delete lines containing a pattern:

sed '/^#/d' file.txt

That strips all comment lines. Combined with blank-line deletion, it’s a quick way to see just the active directives in a config file:

sed '/^#/d;/^$/d' /etc/ssh/sshd_config

Delete a specific line by number:

sed '5d' file.txt

Delete a range:

sed '10,20d' file.txt

Printing Lines: p and -n

By default sed prints every line. The -n flag suppresses that, and the p command prints explicitly. Together they let you extract matching lines, similar to grep but with the full power of sed expressions:

sed -n '/ERROR/p' app.log

Print lines in a range:

sed -n '10,20p' file.txt

Print lines between two patterns:

sed -n '/START/,/END/p' file.txt

That last pattern is something grep alone can’t do. It prints everything between and including the matching boundary lines.

Inserting, Appending, and Changing Lines

Three commands handle structural edits to a file:

  • i inserts text before a matched line
  • a appends text after a matched line
  • c replaces a matched line entirely

Insert a comment before a specific line:

sed '5i # Added by deploy script' file.txt

Append a line after every line matching a pattern:

sed '/^PermitRootLogin/a # Change requires sshd restart' /etc/ssh/sshd_config

Replace an entire line:

sed '/^PermitRootLogin.*/c PermitRootLogin no' /etc/ssh/sshd_config

That last one is cleaner than a substitution when you want to enforce an exact value regardless of what’s currently on that line.

Multiple Expressions

You can chain multiple sed commands in one pass using -e:

sed -e 's/foo/bar/g' -e 's/baz/qux/g' file.txt

Or separate them with semicolons inside a single expression:

sed 's/foo/bar/g;s/baz/qux/g' file.txt

Running multiple substitutions in a single sed pass is faster than piping through multiple sed calls, which matters when processing large log files.

Using Capture Groups

sed supports backreferences in substitutions. Wrap the part you want to capture in ( and ), then reference it as 1, 2, etc.

Swap the order of two words separated by a colon:

echo "hello:world" | sed 's/(.*):(.*)/2:1/'

Output:

world:hello

A practical example: extract the IP address from a log line:

echo "client connected from 192.168.1.50 port 22" | sed 's/.*from ([0-9.]+).*/1/'

Output:

192.168.1.50

If you prefer extended regex (fewer backslashes, cleaner syntax), use -E:

echo "client connected from 192.168.1.50 port 22" | sed -E 's/.*from ([0-9.]+).*/1/'

Transliterate with y

The y command works like tr: it replaces characters one-to-one. Unlike s, it doesn’t use regex. It simply maps each character in the first set to the corresponding character in the second set.

Convert lowercase to uppercase:

echo "hello" | sed 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'

Replace tabs with spaces:

sed 'y/t/ /' file.txt

It’s niche, but when you need a straight character swap without regex overhead, y is the right tool.

Writing Matches to a File

The w command writes matching lines to a separate file. You can use it standalone or as a flag on the s command.

Write all lines containing “ERROR” to a separate file:

sed -n '/ERROR/w errors.log' app.log

Write only lines where a substitution was made:

sed -n 's/foo/bar/gw changes.log' file.txt

This is useful for splitting a file based on content or creating an audit trail of what was changed during a batch edit.

Labels and Branching

For advanced use cases, sed supports labels (:), unconditional branching (b), and conditional branching (t). These let you build loops and basic conditionals.

Add commas to a large number (recursive substitution):

echo "1234567890" | sed -E ':loop; s/([0-9])([0-9]{3})(,|$)/1,23/; t loop'

Output:

1,234,567,890

The :loop label marks a point, t loop branches back to it if the previous s command made a substitution, and the loop stops when no more substitutions are possible.

Join lines ending with a backslash (common in Makefiles and shell scripts):

sed ':a; /$/ { N; s/n//; ta }' file.txt

Branching is where sed starts to feel more like a programming language than a text editor. For anything beyond a simple loop, you’re probably better off reaching for awk or Perl. But for recursive patterns like the comma insertion above, it’s worth knowing.

Real-World Examples

These are patterns I come back to regularly.

Comment out a config directive

sed -i 's/^PasswordAuthentication yes/# PasswordAuthentication yes/' /etc/ssh/sshd_config

Uncomment a line

sed -i 's/^# (PermitRootLogin)/1/' /etc/ssh/sshd_config

Strip trailing whitespace from every line

sed -i 's/[[:space:]]*$//' file.txt

Add a line to the end of a file

sed -i '$a net.ipv4.ip_forward=1' /etc/sysctl.conf

Remove ANSI color codes from log output

sed 's/x1b[[0-9;]*m//g' colored.log

If you’re working with journalctl output or any tool that emits colored text, piping through this sed command gives you clean, parseable output.

Replace a value in a key=value config file

sed -i 's/^(max_connectionss*=s*).*/1500/' /etc/mysql/my.cnf

This is better than a plain substitution because it matches the key regardless of its current value, and preserves any surrounding whitespace.

Extract lines between two timestamps in a log

sed -n '/2025-11-18 14:00/,/2025-11-18 15:00/p' /var/log/app.log

Useful when you’re chasing down what happened during a specific window, like the Cloudflare outage on Nov 18, 2025, or any incident with timestamped logs.

Bulk rename file extensions in a directory

for f in *.txt; do mv "$f" "$(echo "$f" | sed 's/.txt$/.md/')"; done

A quick way to convert a batch of files. You can adapt this pattern for any renaming logic that sed can express.

sed vs awk: When to Use Which

Both tools process text line by line, and there’s overlap. The simple rule I follow: use sed for substitutions, deletions, and line-level edits. Use awk when you need to work with fields, do arithmetic, or write logic with conditionals. If you’re wrestling with structured columnar data, awk wins. If you’re editing config files or transforming text streams, sed is cleaner and faster to write.

Both are worth knowing well. They complement each other and both show up constantly in scripts written by experienced sysadmins. See the 90+ Linux commands frequently used by sysadmins for a broader look at the toolkit.

Useful Options Reference

  • -n: Suppress automatic printing of lines
  • -i: Edit file in-place (add a suffix for backup: -i.bak)
  • -e: Add an expression (can be used multiple times)
  • -f: Read commands from a script file
  • -E or -r: Use extended regular expressions
  • --debug: Print the parsed script and annotate execution (GNU sed 4.6+, useful for understanding complex scripts)

A Note on Portability

GNU sed (the version on Linux) and BSD sed (macOS) differ in a few ways. The most common gotcha: -i on macOS requires a suffix argument, even if it’s empty:

# macOS / BSD sed
sed -i '' 's/foo/bar/g' file.txt

# GNU sed (Linux)
sed -i 's/foo/bar/g' file.txt

If you’re writing scripts that need to run on both, account for this. The Wikipedia entry on sed has a good summary of the differences between implementations. On Linux servers you’ll rarely hit this issue, but it bites people writing cross-platform automation.

Putting It All Together

Here’s a practical script that sanitizes a config template before deployment. It removes comment lines, strips blank lines, sets a hostname, and enables a specific directive:

sed -e '/^#/d' 
    -e '/^$/d' 
    -e "s/^hostname=.*/hostname=$(hostname)/" 
    -e 's/^# (log_level=info)/1/' 
    app.conf.template > app.conf

Four operations, one pass, no temp files. That’s sed working the way it’s designed to. When you’re managing config rollouts across a fleet of servers, patterns like this save a lot of time. Pair it with SCP or rsync to distribute the result and you have a lightweight deployment pipeline without needing any extra tooling.

For anything more complex, such as branching logic or multi-file transforms, look at automation tools like Ansible or a proper templating engine. But for quick, targeted, reliable text edits on Linux, the sed command is hard to beat. It’s been doing this job since 1974 and it’s not going anywhere.

Also see: Boost Your Linux Command Line Productivity, Part 1 and Linux File Permissions Explained for more tips on working faster in the terminal.

Tags: commands, sysadmins

Similar Posts