I have to replace a "pattern" with a "string" on the second-to-last line of the file - file.txt. The below three sed commands are able to print the second-to-last line. But I need to replace a "pattern" with a "string". Any help??
sed -e '$!{h;d;}' -e x file.txt
sed -n 'x;$p' file.txt
sed 'x;$!d' file.txt
$ cat file.txt
cabbage
spinach
collard greens
corn salad
Sweet pepper
kale
How to replace the second-to-last line of a file (Sweet pepper):
a. replace "Sweet" with "green" if second-to-last line contains "Sweet pepper"
b. replace the whole line with "carrots", no matter what it contains
To change Sweet to Green on the second to last line but only if that line contains Sweet pepper
:
$ sed 'x; ${/Sweet pepper/s/Sweet/Green/;p;x}; 1d' file.txt
cabbage
spinach
collard greens
corn salad
Green pepper
kale
To replace the whole of the second to last line, regardless of what it contains, to carrots:
$ sed 'x; ${s/.*/carrots/;p;x}; 1d' file.txt
cabbage
spinach
collard greens
corn salad
carrots
kale
Let's take this command and examine it one step at a time:
sed 'x; ${s/.*/carrots/;p;x}; 1d'
x
This exchanges the pattern space (which holds the most recently read line) and the hold space.
When this is done, the hold space will contain the most recently read line and the pattern space will contain the previous line. (The exception is when we have just read the first line. In that case, the hold space will have the first line and the pattern space will be empty.)
${s/.*/carrots/;p;x}
When we are on the last line, indicated by the $
, the pattern space holds the second to last line and we can perform whatever substitutions or other commands that we like. When we are done, we print the second to last line with p
. Lastly, we swap pattern and hold space again with x
so that the pattern space will again contain the last line. sed
will print this because, by default, at the end of the commands, sed
prints whatever is in the pattern space.
1d
When we are on the first line, indicated by the 1
, the patten space is empty (because there was no previous line) and we delete it (d
).
This method is easy to understand at the cost of slower execution speed:
$ tac file.txt | sed '2 {/Sweet pepper/s/Sweet/Green/}' | tac
cabbage
spinach
collard greens
corn salad
Green pepper
kale
And, for carrots:
$ tac file.txt | sed '2 s/.*/carrots/' | tac
cabbage
spinach
collard greens
corn salad
carrots
kale
How it works: Here, we use tac
to reverse the order of the lines. Observe:
$ tac file.txt
kale
Sweet pepper
corn salad
collard greens
spinach
cabbage
In this way, the second-to-last line becomes line number 2. Thus, we just simply tell sed to operate on line number 2. Afterward, we use tac
again to put the lines but in correct order.
You might find an awk script easier to understand, maintain, port, etc.:
$ awk 'NR==FNR{tgt=NR-1;next} (FNR==tgt) && /Sweet pepper/ { $1="green" } 1' file file
cabbage
spinach
collard greens
corn salad
green pepper
kale
$ awk 'NR==FNR{tgt=NR-1;next} (FNR==tgt) { $0="carrots" } 1' file file
cabbage
spinach
collard greens
corn salad
carrots
kale
Want to change the line 3 before the end instead of the line 1 before the end? That's just the simple, obvious tweak to change -1
to -3
:
$ awk 'NR==FNR{tgt=NR-3;next} (FNR==tgt) { $0="carrots" } 1' file file
cabbage
spinach
carrots
corn salad
Sweet pepper
kale
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With