sed cookbook
Tags: sed, tutorial, cookbook
Published: 2022-02-18 08:07:09

## Table of contents

* [Write the result to another file](#write-the-result-to-another-file)
* [Replace first occurrence on every line](#replace-first-occurrence-on-every-line)
* [Replace values of all occurrences](#replace-values-of-all-occurrences)
* [Replace value of first occurrence in the file](#replace-value-of-first-occurrence-in-the-file)
* [Replace all occurrences on n-th line](#replace-all-occurrences-on-n-th-line)
* [Replace all occurrences within a range of lines](#replace-all-occurrences-within-a-range-of-lines)
* [Replace all occurrences on every line matching pattern](#replace-all-occurrences-on-every-line-matching-pattern)
* [Replace all occurrences on every line NOT matching pattern](#replace-all-occurrences-on-every-line-not-matching-pattern)
* [Replace first occurrence after a pattern](#replace-first-occurrence-after-a-pattern)
* [Delete lines matching pattern](#delete-lines-matching-pattern)
* [Delete n-th line](#delete-n-th-line)
* [Insert before line](#insert-before-line)
* [Insert after line](#insert-after-line)
* [Use other delimiter](#use-other-delimiter)

## Write the result to another file <a name="write-the-result-to-another-file"></a>

Content of `file_1` (before command is executed)

```
Hello World
```

Command

```shell
sed 's/Hello/Goodbye/;w file_2' file_1
```

Content of `file_1` (after command is executed)

```
Hello World
```

Content of `file_2` (after command is executed)

```
Goodbye World
```

## Replace first occurrence on every <a name="replace-first-occurrence-on-every-line"></a>

Input file

```
aa
bb bb
aa
bb
```

Command

```shell
sed 's/bb/dd/' file
```

Output

```
aa
dd bb
aa
dd
```

## Replace values of all occurrences <a name="replace-values-of-all-occurrences"></a>

Input file

```
aa
bb bb
aa
bb
```

Command

```shell
sed 's/bb/dd/g' file
```

Output

```
aa
dd dd
cc
dd
```

## Replace value of first occurrence in the file <a name="replace-value-of-first-occurrence-in-the-file"></a>

Input file

```
aa
bb bb
aa
bb
```

Command

It only works on GNU sed.
See also: https://www.gnu.org/software/sed/manual/html_node/Range-Addresses.html

```shell
sed '0,/bb/{s/bb/dd/}' file
```

Output

```
aa
dd bb
cc
bb
```

## Replace all occurrences on n-th line <a name="replace-all-occurrences-on-n-th-line"></a>

Input file

```
aa
bb bb
aa
bb
```
Command

```shell
# replace all occurrences on 2nd line
sed '2s/bb/dd/g' file
```

Output

```
aa
dd dd
aa
bb
```

## Replace all occurrences within a range of lines <a name="replace-all-occurrences-within-a-range-of-lines"></a>

Input file

```
aa1
aa2
aa3
aa4
aa5
```

Command

```shell
# replace occurrences from 2nd line to 4th line
sed '2,4s/aa/dd/g' file
```

Output

```
aa1
dd2
dd3
dd4
aa5
```

## Replace all occurrences on every line matching pattern <a name="replace-all-occurrences-on-every-line-matching-pattern"></a>

Input file

```
a 1
a 2
a 1 x
b 1
b 2
b 1 x
c 1
c 2
c 1 x
```
Command
```shell
# replace "1" with "10" on all the lines containing "b"
sed '/b/s/1/10/g' file1
```

Output

```
a 1
a 2
a 1 x
b 10
b 2
b 10 x
c 1
c 2
c 1 x
```

## Replace all occurrences on every line NOT matching pattern <a name="replace-all-occurrences-on-every-line-not-matching-pattern"></a>

Input file

```
a 1
a 2
a 1 x
b 1
b 2
b 1 x
c 1
c 2
c 1 x
```

Command

```shell
# replace "1" with "10" on all the lines NOT containing "b"
sed '/b/!s/1/10/g' file
```

Output

```
a 10
a 2
a 10 x
b 1
b 2
b 1 x
c 10
c 2
c 10 x
```

## Replace first occurrence after a pattern <a name="replace-first-occurrence-after-a-pattern"></a>

Input file

```yml
server-alpha:
    host: '192.168.0.1'
    port: 9090

server-beta:
    host: '192.168.0.1'
    port: 9091

server-charlie:
    host: '192.168.0.1'
    port: 9092
```

Problem

Change the host of `server-beta` to `192.168.0.2`. Values of other hosts remain unchanged.

Command

```shell
sed -e '/server-beta/! b' \
    -e ':label1' \
    -e 's/192.168.0.1/192.168.0.2/' \
    -e 't label2' \
    -e 'n' \
    -e 'b label1' \
    -e ':label2' \
    -e 'n' \
    -e 'b label2' \
    file
```

Output

```yml
server-alpha:
    host: '192.168.0.1'
    port: 9090

server-beta:
    host: '192.168.0.2'
    port: 9091

server-charlie:
    host: '192.168.0.1'
    port: 9092
```

Explanation

It uses the sed [branching commands](https://www.gnu.org/software/sed/manual/html_node/Branching-and-flow-control.html#Branching-and-flow-control) `b` and `t`.

* `/server-beta/! b` - if pattern space doesn't contain `server-beta`, it will jump to the end of script.

* `:label1` - declare a label with name `label1`

* `s/192.168.0.1/192.168.0.2/` - Replace `192.168.0.1` with `192.168.0.2`

* `t label2` - if the previous replacement is done, jump to `label2`.

* `n` - read next line from input and put to pattern space or exit if no more line from input

* `b label1` - jump to `label1`

* `:label2` - declare a label with name `label2`

* `n` - read next line from input and put to pattern space or exit if no more line from input

* `b label2` - jump to `label2`

Pseudo code

```java
input = readLineFromInput()
while (input != null) {
    patternSpace.append(input)

    if (patternSpace.contains("server-beta")) {
        while (true) { // label1
            if (patternSpace.replace("192.168.0.1", "192.168.0.2")) {
                break // jump to label2
            }
            /** start of n **/
            display(patternSpace)
            patternSpace.clear()
            input = readLineFromInput()
            if (input == null) {
                exit
            }
            patternSpace.append(line)
            /** end of n **/
        }

        while (true) { // label2
            /** start of n **/
            display(patternSpace)
            patternSpace.clear()
            input = readLineFromInput()
            if (input == null) {
                exit
            }
            patternSpace.append(line)
            /** end of n **/
        }
    }

    display(patternSpace)
    patternSpace.clear()
    input = readLineFromInput()
}
```

## Delete lines matching pattern <a name="delete-lines-matching-pattern"></a>

Input file

```
aa1
bb2
cc3
```

Command

```shell
sed /bb/d file
```

Output

```
aa1
cc3
```

## Delete n-th line <a name="delete-n-th-line"></a>

Input file

```
aa1
aa2
aa3
aa4
aa5
```

Command

```shell
# delete 2nd line
sed 2d
```

Output

```
aa1
aa3
aa4
aa5
```

## Insert before line <a name="insert-before-line"></a>

Input file

```
aa1
bb2
cc3
```

Command

```shell
sed "/bb/i bb_before" file
```

Output

```
aa1
bb_before
bb2
cc3
```

## Insert after line <a name="insert-after-line"></a>

Input file

```
aa1
bb2
cc3
```

Command

```shell
sed "/bb/a bb_after" file
```

Output

```
aa1
bb2
bb_after
cc3
```

## Use other delimiter <a name="use-other-delimiter"></a>

Input file

```
/aa/bb/cc/dd
```

Problem

It is too clumsy to escape slash.

```shell
sed 's/bb/bb1\/bb2\/bb3/' file
```

Command

It is clearer if `#` is used.

```shell
sed 's#bb#bb1/bb2/bb3#' file
```

Output

```
/aa/bb1/bb2/bb3/cc/dd
```

Actually any character can be used.

These are all valid and produce same output.

```shell
# , is used
sed 's,bb,bb1/bb2/bb3,' file

# $ is used
sed 's$bb$bb1/bb2/bb3$' file

# a is used
sed 'sabbabb1/bb2/bb3a' file
```

But you can't use character which is inside the pattern.

```shell
# b is used
sed 'sbbbbbb1/bb2/bb3b' file
# sed: -e expression #1, char 5: unknown option to `s'

# 1 is used
sed 's1bb1bb1/bb2/bb31' file
# sed: -e expression #1, char 9: unknown option to `s'
```