aborted
aborted

Reputation: 4531

Using sed to handle a complete section of text

I am trying bash scripting for the first time and I'm stuck on using sed to alter a config file. The keys that I need to edit are pretty generic and used throughout the file, so I have to rely on a section to know where to do my "touching".

So, as said, there are multiple sections around the file, declared by [section name] and then under it, you have the configurations for that section, indented by one tab. There are keys like enabled, type etc. which are used all over the file.

So a complete section would look like this:

[backend]
        # enabled = no
        # data source = average
        # type = graphite
        # destination = localhost
        # prefix = netdata
        # hostname = localhost
        # update every = 10
        # buffer on failures = 10
        # timeout ms = 20000

So what I need to do is:

  1. Uncomment the whole section
  2. Change enabled = no to enabled = yes
  3. Change destination's value to an IP address with a port (192.168.99.38:2003)
  4. Change hostname's value to another hostname, maybe the machine's hostname ($HOSTNAME?).

Thing is, I'm not sure how to tackle this at all. I have looked around for sed multiline handling, but I definitely don't know how to match the [backend] section only. This is very new to me.

Upvotes: 0

Views: 121

Answers (3)

Shakiba Moshiri
Shakiba Moshiri

Reputation: 23884

As you comment me: Only what's under [backend], nothing else

It may interest you doing that with Perl and if not just comment me; I will delete and the answer.

Say you have this file:

[no-backend]
        # enabled = no
        # data source = average
        # type = graphite
        # destination = localhost
        # prefix = netdata
        # hostname = localhost
        # update every = 10
        # buffer on failures = 10
        # timeout ms = 20000

[backend]
        # enabled = no
        # data source = average
        # type = graphite
        # destination = localhost
        # prefix = netdata
        # hostname = localhost
        # update every = 10
        # buffer on failures = 10
        # timeout ms = 20000

[no-backend]
        # enabled = no
        # data source = average
        # type = graphite
        # destination = localhost
        # prefix = netdata
        # hostname = localhost
        # update every = 10
        # buffer on failures = 10
        # timeout ms = 20000  

This one-liner finds that section for you:

perl  -lne '$b=$.; $e=($b+10) if /\[backend\]/;print if $b++<$e' file

or readable version
perl -lne 'if( /\[backend\]/ ){ $b=$.; $e=( $b+10 ); }; if( $b++ < $e ){ print }' file

and the output:

[backend]
        # enabled = no
        # data source = average
        # type = graphite
        # destination = localhost
        # prefix = netdata
        # hostname = localhost
        # update every = 10
        # buffer on failures = 10
        # timeout ms = 20000

and now instead of print you can modify that section with:

s/#//;s/no/yes/;s/(?<=destination = ).+$/192.168.99.38:2003/;s/(?<=hostname = ).+$/$HOSTNAME/

the full one-liner

perl -lpe 'if(/\[backend\]/){$b=$.;$e=($b+10);};if($b++<$e){ s/#//;s/no/yes/;s/(?<=destination = ).+$/192.168.99.38:2003/;s/(?<=hostname = ).+$/\$HOSTNAME/ }' file

and the output:

[no-backend]
        # enabled = no
        # data source = average
        # type = graphite
        # destination = localhost
        # prefix = netdata
        # hostname = localhost
        # update every = 10
        # buffer on failures = 10
        # timeout ms = 20000
[backend]
         enabled = yes
         data source = average
         type = graphite
         destination = 192.168.99.38:2003
         prefix = netdata
         hostname = $HOSTNAME
         update every = 10
         buffer on failures = 10
         timeout ms = 20000

[no-backend]
        # enabled = no
        # data source = average
        # type = graphite
        # destination = localhost
        # prefix = netdata
        # hostname = localhost
        # update every = 10
        # buffer on failures = 10
        # timeout ms = 20000

and finally after checking the output if everything was good, then you can use -i option to use edit-in-place feature, like:

perl -i.bak -lne '...the rest of the script...' file

.bak is just for getting backup of your old file. ( like: file.txt.bak )


UPDATE for your comment

perl -lpe '$hn=qx(cat /etc/hostname);chomp $hn;if(/\[backend\]/){$b=$.;$e=($b+10);};if($b++<$e){s/#//;s/no/yes/;s/(?<=destination = ).+$/192.168.99.38:2003/;s/(?<=hostname = ).+$/$hn/ }' file  

and the output:

...
...
[backend]
         enabled = yes
         data source = average
         type = graphite
         destination = 192.168.99.38:2003
         prefix = netdata
         hostname = k-five
         update every = 10
         buffer on failures = 10
         timeout ms = 20000
...
...

Upvotes: 1

Raman Sailopal
Raman Sailopal

Reputation: 12887

Assuming the text is in configfile and there is another section underneath starting with "[", the solution below should work

sed -i '/\[backend\]/,/\[/{s/#//;s/enabled = no/enabled = yes/;s/\(destination = \)\(.*\)\($\)/\1192\.168\.99\.38\:2003\3/;s/\(hostname = \)\(.*\)\($\)/\1'$HOSTNAME'\3/}' configfile

We search for the text between [backend] and [ and then execute the sed commands enclosed in {} We remove # and then edit the destination as well as finally the hostname.

If there are no other sections below backend, change the command to:

sed -i '/\[backend\]/,/^$/{s/#//;s/enabled = no/enabled = yes/;s/\(destination = \)\(.*\)\($\)/\1192\.168\.99\.38\:2003\3/;s/\(hostname = \)\(.*\)\($\)/\1'$HOSTNAME'\3/}' configfile

Upvotes: 1

Michael Vehrs
Michael Vehrs

Reputation: 3363

sed is not the ideal tool for this kind of stuff, but it can be done:

/^\[.*]$/h
{G;/\n\[backend\]$/{
  s/#//
  /enabled/s/no/yes/
  /destination/s/localhost/whatever/
  /hostname/s/localhost/something else/
  }
  s/\n\[.*\]$//
}

Upvotes: 1

Related Questions