5

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 occurence 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?

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



EDITED:
The repeated lines can be separeted 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"
Luca Borrione
  • 16,324
  • 8
  • 52
  • 66

4 Answers4

6

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
glenn jackman
  • 238,783
  • 38
  • 220
  • 352
  • I personally find this an excellent idea and it's still easy to read. I personalized it a little bit and added the ability to write directly to the file, (is there a way to use your solution with interactive -i ?). My command finally is `tac $file | awk '/ScriptAlias \/cgi-bin\// && ! seen {print "ScriptAlias /cgi-bin/ \"mypath\""; seen=1} {print}' | tac > "/var/lock/${file}" && mv "/var/lock/${file}" "${file}"` – Luca Borrione Apr 11 '12 at 13:51
  • Cannot write to the input file directly. You have to use a temp file (as you did). That's what `sed -i` does in the background – glenn jackman Apr 11 '12 at 14:23
1

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
Kent
  • 189,393
  • 32
  • 233
  • 301
0
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>
Vijay
  • 65,327
  • 90
  • 227
  • 319
0

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
pizza
  • 7,296
  • 1
  • 25
  • 22