Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Replace the "pattern" on second-to-last line of a file

Tags:

sed

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

like image 576
Arun Avatar asked Jul 29 '16 01:07

Arun


2 Answers

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

How it works

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).

A still simpler method

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.

like image 155
John1024 Avatar answered Sep 26 '22 01:09

John1024


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
like image 25
Ed Morton Avatar answered Sep 24 '22 01:09

Ed Morton