2014-11-18
Finding free numbers in a range, crudely, with Unix tools
Suppose, not entirely hypothetically, that you have the zone file for the reverse mapping of a single /24 subnet (in Bind format) and you want to find a free IP address in that subnet. The first field in the zone file is the last octet of the IP (ie for '127.0.0.1' it is '1'), so this problem reduces to finding what numbers are not used in the file out of the range 1 to 254.
So let's do this really crudely with readily available Unix tools:
grep '^[0-9]' ptrzone | awk '{print $1}' | sort >/tmp/used seq 1 254 | sort >/tmp/all comm -13 /tmp/used /tmp/all | sort -n
(We must do a 'sort -n
' only at the end because of a stupid GNU
comm
decision.)
We can get rid of one of the two files here by piping directly into
comm
, but unfortunately we can't get rid of both of them in
standard Bourne shell. In Bash and zsh (and AT&T ksh), we can use
what they each call 'process substitution' to directly embed each
pipeline into the comm
arguments:
comm -13 <(grep '^[0-9]' ptrzone | awk '{print $1}' | sort) <(seq 1 254 | sort) | sort -n
(This also works in rc
, with a
different syntax.)
Once upon a time I would have happily written the single-line version. These days my tendencies are towards the multi-line version unless I'm feeling excessively clever, partly because it's easier to check the intermediate steps.
(A neater version would condense runs of numbers into ranges, but I don't think you can do that simply with any existing common Unix tools. Perl experts can maybe do it in a single line.)
PS: One hazard of a very well stuffed $HOME/bin
directory is that
it turns out I already had a command to do this for me (and with
the neater range-based output). Oh well, such things happen. Perhaps
I should thin out my $HOME/bin
on the grounds of disuse of much
of it, but it's always hard to get rid of your personal little
scripts.