Remove zone block from wildcard binding configuration file from command line

I am trying to figure out how to make a script that will delete a specific zone in my Bind zone file.

I have about 200 domains in my zone file and I thought I could make a script that would delete a specific domain for me. On my master server, the zone file looks like this:

zone "example.com" {
    type master;
    file "master/example.com";
};

zone "example.net" {
    type master;
    file "master/example.net";
};

      

However, my slave has two end brackets instead of one.

zone "example.com" {
    type slave;
    file "slave/example.com";
    masters {
            10.0.0.1;
    };
};

zone "example.net" {
    type slave;
    file "slave/example.net";
    masters {
            10.0.0.1;
    };
};

      

So I could technically grep example.com, take that line and every line before, };

and delete. The same goes for the slave only there, it will have to delete every line up to the second};

This will work with the master:

sed -n -e '/\"example.net\"/,/\};/p' zones.local

      

But not on a slave. Does anyone know how to fix this? The zoned file looks the same and there are no comments or anything like that.

+3


source to share


4 answers


I would use Perl for my recursive regexes. We can use them according to the curly brace block, from the open parenthesis to the corresponding closing parenthesis. For example:

perl -i -0777 -pe 's/zone\s*"example.net"\s*(\{([^{}]|(?1))*\});\s*//g' foo.conf

      

The tricky part of the regular expression (\{([^{}]|(?1))*\})

. This is captured as capturing group 1, and is recursively recursively referenced within itself as (?1)

, so it matches a group enclosed in curly braces in an arbitrary number of many unbound and curly brace nested groups.

As long as there are no parentheses in comments or lines, this should survive all your formatting changes.

Update: I had to look a bit, but there are BIND file parsers out there that offer a more robust solution to the problem - ultimately, regexes (even recursive ones) can only carry you so far, and in some special cases. In general, it is better to use the correct parsers for structured data like this, even if the code gets longer for it. For example, with the BIND :: Config :: Parser module, you can write



#!/usr/bin/perl

use BIND::Config::Parser;

my $parser = new BIND::Config::Parser;

my $indent = 0;
my $ignoredepth = 0;
my $file = $ARGV[0];
my $domain = "\"$ARGV[1]\"";

$parser->set_open_block_handler(
    sub {
        if(($_[0] eq "zone" && $_[1] eq $domain) || $ignoredepth != 0) {
            ++$ignoredepth;
        } else {
            print "  " x $indent, join(" ", @_), " {\n";
            ++$indent;
        }
    });

$parser->set_close_block_handler(
    sub {
        if($ignoredepth != 0) {
            --$ignoredepth;
        } else {
            --$indent;
            print "  " x $indent, "};\n";
        }
    });

$parser->set_statement_handler(
    sub {
        if($ignoredepth == 0) {
            print "  " x $indent, join(" ", @_), "\;\n";
        }
    });

$parser->parse_file($file);

      

and call

perl remove_zone.pl named.conf.local example.net

      

This will be a nuke comment, but keep the functional parts of the config file intact, no matter what the regexp might confuse there. This is the best way for automatic use.

+1


source


This zone file obeys Tcl syntax, so we can define a procedure named "zone" and read the zone file as if it were a Tcl script:

#!/usr/bin/env tclsh
lassign $argv domain_to_remove zone_file
proc zone {domain body} {
    if {$domain ne $::domain_to_remove} {
        puts [format {zone "%s" {%s}} $domain $body]
    }
}
source $zone_file

      



and then

$ tclsh remove_zone.tcl example.com zones 
zone "example.net" {
    type slave;
    file "slave/example.net";
    masters {
            10.0.0.1;
    };
}

      

+1


source


This sed trick will work for both master and node if your blocks end with a literal };\n

:

sed -n -e '/\"example.com\"/,/^\};$/d;p;' zones.local

      

However, since it makes assumptions about how your file is structured, I would recommend the perl

by @Wintermute option.

0


source


sed is NOT for anything involving multiple lines, for simple single line substitutions. All seds Thai language constructs for handling multiple input lines at the same time became obsolete in the mid-1970s when awk was invented, in the past time to let them go:

$ awk -v RS= -v ORS='\n\n' '/example.com/' file
zone "example.com" {
    type master;
    file "master/example.com";
};

      

With all your samples in one file:

$ cat file
zone "example.com" {
    type master;
    file "master/example.com";
};

zone "example.net" {
    type master;
    file "master/example.net";
};

zone "example.com" {
    type slave;
    file "slave/example.com";
    masters {
            10.0.0.1;
    };
};

zone "example.net" {
    type slave;
    file "slave/example.net";
    masters {
            10.0.0.1;
    };
};

      

...

$ awk -v RS= -v ORS='\n\n' '/example.com/' file
zone "example.com" {
    type master;
    file "master/example.com";
};

zone "example.com" {
    type slave;
    file "slave/example.com";
    masters {
            10.0.0.1;
    };
};

      

...

$ awk -v RS= -v ORS='\n\n' '/example.net/' file
zone "example.net" {
    type master;
    file "master/example.net";
};

zone "example.net" {
    type slave;
    file "slave/example.net";
    masters {
            10.0.0.1;
    };
};

      

0


source







All Articles