Luca Borrione
Luca Borrione

Reputation: 16992

How to replace only the last occurrence in a file?

Having a file containing repeated commented lines like:

# ScriptAlias /cgi-bin/ "somepath"
# ScriptAlias /cgi-bin/ "otherpath"

I want to add a line only after the last occurrence resulting in

# ScriptAlias /cgi-bin/ "somepath"
# ScriptAlias /cgi-bin/ "otherpath"
ScriptAlias /cgi-bin/ "mypath"

To do so I'm using this command:

sed -i 's:^\(.*ScriptAlias /cgi-bin/.*\):\1 \nScriptAlias /cgi-bin/ "mypath":' file

But this results in adding my line after each occurence like:

# ScriptAlias /cgi-bin/ "somepath"
ScriptAlias /cgi-bin/ "mypath"
# ScriptAlias /cgi-bin/ "otherpath"
ScriptAlias /cgi-bin/ "mypath"

How can I tell sed to replace only the last occurence?

If there's no way to solve it using sed (as said in comments), please provide alternatives reaching the same result, thanks.

The repeated lines can be separated and with other lines between them like

# ScriptAlias /cgi-bin/ "somepath"
# ScriptAlias /cgi-bin/ "otherpath"

# ScriptAlias /cgi-bin/ "another-path"
ScriptAlias /foo/ "just-jump"
# ScriptAlias /cgi-bin/ "that's the last"

Upvotes: 5

Views: 6563

Answers (4)

pizza
pizza

Reputation: 7630

It is a task for the editor.

ex input_file << "DONE"
/ScriptAlias \/cgi-bin\/ "otherpath"
a
ScriptAlias /cgi-bin/ "mypath"
.
:1
/ScriptAlias \/cgi-bin\/ "another-path"
a
ScriptAlias /cgi-bin/ "just-jump"
.
:x
DONE

In last occurence mode.

ex input_file << "DONE"
$
?ScriptAlias \/cgi-bin\/ "otherpath"
a
ScriptAlias /cgi-bin/ "mypath"
.
$
?ScriptAlias \/cgi-bin\/ "another-path"
a
ScriptAlias /cgi-bin/ "just-jump"
.
:x
DONE

Upvotes: 0

Vijay
Vijay

Reputation: 67221

tail -r temp | awk '{line="yourline"}{if($0~/ScriptAlias/&&last==0){print line"\n"$0;last=1}else print}' | tail -r

tested below:

krithika.337> cat temp
# ScriptAlias /cgi-bin/ "somepath" 
# ScriptAlias /cgi-bin/ "otherpath" 
# ndmxriptAlias /cgi-bin/ "otherpath" 
# ScriptAlias /cgi-bin/ "otherpath" 
# bdjiptAlias /cgi-bin/ "otherpath" 
krithika.338> tail -r temp | awk '{line="yourline"}{if($0~/ScriptAlias/&&last==0){print line"\n"$0;last=1}else print}' | tail -r
# ScriptAlias /cgi-bin/ "somepath" 
# ScriptAlias /cgi-bin/ "otherpath" 
# ndmxriptAlias /cgi-bin/ "otherpath" 
# ScriptAlias /cgi-bin/ "otherpath" 
yourline
# bdjiptAlias /cgi-bin/ "otherpath" 
krithika.339>

Upvotes: 0

glenn jackman
glenn jackman

Reputation: 246807

Use tac so you print your new line the first time you see the pattern:

tac file | awk '/ScriptAlias/ && ! seen {print "new line"; seen=1} {print}' | tac

Upvotes: 6

Kent
Kent

Reputation: 195059

alternative with awk:

awk '/ScriptAlias \/cgi-bin\//{x=NR} {a[NR]=$0;}END{for(i=1;i<=NR;i++){if(i==x+1)print "$$$here comes new line$$$"; print a[i];}}' file

test:

kent$  echo "# ScriptAlias /cgi-bin/ "somepath"
fooo
# ScriptAlias /cgi-bin/ "otherpath"
bar
"|awk '/ScriptAlias \/cgi-bin\//{x=NR} {a[NR]=$0;}END{for(i=1;i<=NR;i++){if(i==x+1)print "$$$here comes new line$$$"; print a[i];}}'

output:

# ScriptAlias /cgi-bin/ somepath
fooo
# ScriptAlias /cgi-bin/ otherpath
$$$here comes new line$$$
bar

Upvotes: 1

Related Questions