sed Command: String Substitution, Deleting Lines and Using the -i Option
The sed command is a Linux tool that reads a file line by line and runs substitutions, deletions and insertions, so you can change values in a configuration file without opening an editor. When we ran sed on a copy of a WordPress wp-config.php on an AlmaLinux 9 server, we could change the database name while keeping the original with -i.bak.
The error messages in this article are shown as printed under an English locale.
1️⃣ Basic sed command usage and s substitution
The most commonly used s command substitutes strings in the form [s/search string/replacement string/]. It replaces only the first match on a line.
echo 'http://example.com http://example.com/blog' | sed 's/http:/https:/'https://example.com http://example.com/blogTo replace every match on a line, add the g flag at the end.
echo 'http://example.com http://example.com/blog' | sed 's/http:/https:/g'https://example.com https://example.com/blogIf you add a file name at the end, sed reads that file and prints the changed result. On a copy of WordPress's wp-config-sample.php made as wp-config.php, we changed the DB_USER value and checked just that line with grep.
sed 's/username_here/wp_user/' wp-config.php | grep DB_USERdefine( 'DB_USER', 'wp_user' );Since the value changed in the output, it is easy to assume the file changed too, but without options sed only prints the result to the screen and leaves the original file as it is. When we looked up the same line in the file again, the original value was still there.
grep DB_USER wp-config.phpdefine( 'DB_USER', 'username_here' );To change the original file as well, use the -i option from 4️⃣.
2️⃣ Selecting lines with sed by line number and pattern (-n, p)
Without an address, the s command applies to every line, so to work on specific lines, write a line number or pattern as an address in front of the command. -n turns off automatic printing and p prints the selected lines. Used together, they show line 23 alone.
sed -n '23p' wp-config.phpdefine( 'DB_NAME', 'database_name_here' );Joining two line numbers with a comma prints that range. Lines 23 to 32 are the section with the database name, user, password and host settings.
sed -n '23,32p' wp-config.phpdefine( 'DB_NAME', 'database_name_here' );
/** Database username */
define( 'DB_USER', 'username_here' );
/** Database password */
define( 'DB_PASSWORD', 'password_here' );
/** Database hostname */
define( 'DB_HOST', 'localhost' );If you write a pattern between slashes instead of a line number, every line containing that pattern is selected.
sed -n '/DB_/p' wp-config.phpdefine( 'DB_NAME', 'database_name_here' );
define( 'DB_USER', 'username_here' );
define( 'DB_PASSWORD', 'password_here' );
define( 'DB_HOST', 'localhost' );
define( 'DB_CHARSET', 'utf8mb4' );
define( 'DB_COLLATE', '' );The [=] command prints the number of the selected line. The address $ means the last line, so [$=] shows the total number of lines in the file.
sed -n '/DB_HOST/=' wp-config.php32sed -n '$=' wp-config.php102An address can also go in front of the s command. Below is the result of changing localhost only on line 32 and printing only the changed line with the p flag.
sed -n '32s/localhost/127.0.0.1/p' wp-config.phpdefine( 'DB_HOST', '127.0.0.1' );Running p without -n printed the selected line one more time. sed reads lines one at a time into the pattern space, runs the commands, then prints the pattern space automatically, so line 2 printed by p appears again through automatic printing.
sed '2p' wp-config.php | head -n 4<?php
/**
/**
* The base configuration for WordPressThis line-at-a-time method is like a factory conveyor belt that places one part at a time on a workbench, processes it, and moves on to the next part.
You can compare what each command prints for each line, step by step, in the diagram below.
3️⃣ Deleting and inserting lines with sed (d, i, a)
d deletes the selected lines. To view the nginx sample configuration below without comment lines, select lines that start with # or have only spaces before # using the pattern [^ *#].
cat example.conf# example.com
server {
listen 80;
server_name example.com www.example.com;
# document root
root /var/www/example.com;
index index.php index.html;
location / {
try_files $uri $uri/ /index.php?$args;
}
}sed '/^ *#/d' example.confserver {
listen 80;
server_name example.com www.example.com;
root /var/www/example.com;
index index.php index.html;
location / {
try_files $uri $uri/ /index.php?$args;
}
}Joining commands with a semicolon also deletes [^$], which means an empty line. The two empty lines left after deleting the comment lines disappeared, and only the configuration lines remained.
sed '/^ *#/d; /^$/d' example.confserver {
listen 80;
server_name example.com www.example.com;
root /var/www/example.com;
index index.php index.html;
location / {
try_files $uri $uri/ /index.php?$args;
}
}i inserts text before the selected line and a inserts it after the selected line. The result of inserting one comment line before line 1 is shown below.
sed '1i # managed by sed' example.conf | head -n 3# managed by sed
# example.com
server {It is easy to assume that indented text after a keeps its indentation, but GNU sed skips the spaces right after a and inserts only the text.
sed '/index index.php/a client_max_body_size 64M;' example.conf# example.com
server {
listen 80;
server_name example.com www.example.com;
# document root
root /var/www/example.com;
index index.php index.html;
client_max_body_size 64M;
location / {
try_files $uri $uri/ /index.php?$args;
}
}To keep the indentation, add a backslash after a. Everything from the space after the backslash goes in as text.
sed '/index index.php/a\ client_max_body_size 64M;' example.conf# example.com
server {
listen 80;
server_name example.com www.example.com;
# document root
root /var/www/example.com;
index index.php index.html;
client_max_body_size 64M;
location / {
try_files $uri $uri/ /index.php?$args;
}
}c replaces the selected line entirely. Below are the first four lines of the result of changing the listen 80 line to 8080.
sed '/listen 80;/c\ listen 8080;' example.conf | head -n 4# example.com
server {
listen 8080;
server_name example.com www.example.com;4️⃣ Editing files in place with sed -i and backup file names
-i saves the result to the original file instead of the screen. Text attached right after -i is added to the end of the backup file name. wp-config.php and other files the site reads are best edited with -i.bak, keeping the original.
cp wp-config-sample.php wp-config.phpsed -i.bak 's/database_name_here/wordpress_db/' wp-config.phpRunning with -i prints nothing to the screen, so check the files with ls -l.
ls -l wp-config.php*-rw-r--r-- 1 root root 3333 Oct 28 15:13 wp-config.php
-rw-r--r-- 1 root root 3339 Oct 28 15:13 wp-config.php.bakThe edited file and a .bak file now exist side by side. The size is 6 bytes smaller because database_name_here became the shorter wordpress_db. Comparing the two files with diff shows only the changed line.
diff wp-config.php.bak wp-config.php23c23
< define( 'DB_NAME', 'database_name_here' );
---
> define( 'DB_NAME', 'wordpress_db' );The changed line is the single DB_NAME line on line 23.
If you have several values to change, chain scripts with -e and run them at once.
sed -i -e 's/username_here/wp_user/' -e 's/password_here/wp_pass/' wp-config.phpgrep -n -e DB_USER -e DB_PASSWORD wp-config.php26:define( 'DB_USER', 'wp_user' );
29:define( 'DB_PASSWORD', 'wp_pass' );If you join -i and -e as [-ie], the e is read as part of the backup name, and a wp-config.phpe file appeared as shown below.
sed -ie 's/127.0.0.1/localhost/' wp-config.phpls wp-config.php*wp-config.php
wp-config.php.bak
wp-config.phpe-i writes the edited content to a new file and then renames it over the original name, so the file's inode number differs before and after the run.
ls -i wp-config.php207618887 wp-config.phpsed -i 's/localhost/127.0.0.1/' wp-config.phpls -i wp-config.php207618883 wp-config.phpThe inode number changed from 207618887 before the run to 207618883.
If the file does not exist or you leave out the file name, sed ends with the error below and no file changes.
sed -i 's/x/y/' no-such.phpsed: can't read no-such.php: No such file or directorysed -i 's/x/y/'sed: no input files5️⃣ sed regular expressions, changing the delimiter and options
🔲 Regular expressions and changing the delimiter
When you change a value containing slashes, such as a path, the s command's delimiter gets mixed up with the slashes in the value, so each slash in the value needs a backslash.
sed -n 's/\/var\/www\/example.com/\/srv\/www\/example.com/p' example.conf root /srv/www/example.com;The character right after s becomes the delimiter, so changing to |, which is not in the value, gives the same result without backslashes.
sed -n 's|/var/www/example.com|/srv/www/example.com|p' example.conf root /srv/www/example.com;A dot (.) in a pattern matches any single character. Below is the result of a pattern looking for example.com that also changed exampleXcom.
echo 'exampleXcom example.com' | sed 's/example.com/test.org/g'test.org test.orgTo find a literal dot, put a backslash in front of it.
echo 'exampleXcom example.com' | sed 's/example\.com/test.org/g'exampleXcom test.orgA part wrapped in parentheses is recalled in the replacement string with [\1]. With -E, you can use parentheses and + without backslashes. Below is the result of keeping the number after listen and appending default_server.
sed -nE 's/listen ([0-9]+);/listen \1 default_server;/p' example.conf listen 80 default_server;Running the same script without -E reads the parentheses as literal text, giving an error that there is no group to recall. To work without -E, put backslashes on the parentheses and +.
sed -n 's/listen ([0-9]+);/listen \1 default_server;/p' example.confsed: -e expression #1, char 46: invalid reference \1 on `s' command's RHSsed -n 's/listen \([0-9]\+\);/listen \1 default_server;/p' example.conf listen 80 default_server;[&] in the replacement string means all the text the pattern matched, so you can add # in front of the matched part to turn that line into a comment.
sed -n 's/server_name .*;/# &/p' example.conf # server_name example.com www.example.com;🔲 sed command options
📌 Main options: results from this article
| Option | Task | Result in this article | |
|---|---|---|---|
| -n | Turns off automatic printing and prints only lines selected with p | sed -n '23p' shows line 23 alone; without -n, 2p prints line 2 twice | |
| -i | Saves the result to the original file instead of the screen (text right after it is added to the end of the backup name) | -i.bak creates wp-config.php.bak; the inode number changes after the run | |
| -e | Chains several scripts and runs them at once | DB_USER and DB_PASSWORD change at once; joined as -ie, a wp-config.phpe backup appears | |
| -E | Uses extended regular expressions (no backslashes on parentheses and +) | Prints listen 80 default_server;, and the same script without -E gives an [invalid reference] error | |
| --follow-symlinks | With -i, edits the original file the link points to | Without it, a regular file replaces the link and the original stays the same; with it, the original value changes |
📎 Other options: per the official manual
| Option | Task | |
|---|---|---|
| -f | Reads the script from a file and runs it | |
| -r | Extended regular expressions, the same as -E (-E is more portable) | |
| -s | Treats each file separately instead of as one continuous stream | |
| -z | Splits lines on the null character instead of a newline | |
| -u | Reads input in smaller amounts and flushes output more often | |
| --posix | Turns off all GNU extensions | |
| --debug | Prints the execution in expanded form |
🔲 sed command simulator
The working folder of the simulator below holds a copy of wp-config.php and example.conf. You can type the commands so far yourself, and check files changed with -i using ls -l and diff.
🔢 FAQ & Recommended Content







ℹ️ Affiliate Disclosure
This site's content contains affiliate links. When a visitor buys a product or service through one of them, the site receives a commission from the seller. The amount the buyer pays(it goes down during event discounts ↓)does not go up. Posted prices, discounts, and stock reflect the time of writing and may differ, so confirm with the seller before buying. Products are chosen and reviewed by our own standards, and commissions do not affect the order or content of reviews.