I'm trying to figure out how to make a script that will delete a specific zone in my Bind zone-file.
I have around 200 domains in my zone-file and I was thinking I could make a script that will 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 on my slave there are 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 until }; and delete. The same goes for the slave server only there it will have to delete every line until the second };
This would work on the master:
sed -n -e '/\"example.net\"/,/\};/p' zones.local
But not on the slave. Does anyone know how to fix this? The zone-file looks the same and there's no comments or anything like that.
That zone file obeys the syntax of Tcl, so we can define a procedure named "zone" and read the zone file as if it's 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;
};
}
I'd use Perl for its recursive regexes. We can use them to match a brace-enclosed block, from an opening brace to the corresponding closing brace. For example:
perl -i -0777 -pe 's/zone\s*"example.net"\s*(\{([^{}]|(?1))*\});\s*//g' foo.conf
The tricky part of the regex is (\{([^{}]|(?1))*\}). This is captured as capturing group 1 and referenced recursively inside itself as (?1), so it matches a brace-enclosed group of arbitrary many non-brace characters and brace-enclosed groups.
As long as there are no braces in comments or strings, this should survive all your formatting changes.
Update: I had to look a bit, but there are BIND config file parsers out there that offer a more robust solution to the problem -- ultimately, regexes (even recursive ones) can carry you only so far and will break in some special cases. In general, it is better to use proper parsers for structured data like this, even if the code becomes longer for it. For example, with the BIND::Config::Parser Perl module, you could 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 nuke comments but keep the functional parts of the config file intact no matter what's in there that could confuse a regex. For automated use, this is the better way.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With