sed replace last line matching pattern

Another approach:

sed "`grep -n '^a$' a | cut -d \: -f 1 | tail -1`s/a/c/" a

The advantage of this approach is that you run sequentially on the file twice, and not read it to memory. This can be meaningful in large files.


Not quite sed only:

tac file | sed '/a/ {s//c/; :loop; n; b loop}' | tac

testing

% printf "%s\n" a b a b a b | tac | sed '/a/ {s//c/; :loop; n; b loop}' | tac
a
b
a
b
c
b

Reverse the file, then for the first match, make the substitution and then unconditionally slurp up the rest of the file. Then re-reverse the file.

Note, an empty regex (here as s//c/) means re-use the previous regex (/a/)

I'm not a huge sed fan, beyond very simple programs. I would use awk:

tac file | awk '/a/ && !seen {sub(/a/, "c"); seen=1} 1' | tac

Many good answers here; here's a conceptually simple two-pass sed solution assisted by tail that is POSIX-compliant and doesn't read the whole file into memory, similar to Eran Ben-Natan's approach:

sed "$(sed -n '/a/ =' file | tail -n 1)"' s/a/c/' file
  • sed -n '/a/=' file outputs the numbers of the lines (function =) matching regex a, and tail -n 1 extracts the output's last line, i.e. the number of the line in file file containing the last occurrence of the regex.

  • Placing command substitution $(sed -n '/a/=' file | tail -n 1) directly before ' s/a/c' results in an outer sed script such as 3 s/a/c/ (with the sample input), which performs the desired substitution only on the last on which the regex occurred.

If the pattern is not found in the input file, the whole command is an effective no-op.

Tags:

Regex

Bash

Sed