Search:

IP-Nummern sortieren

Wenn man mit IP-Nummern zu tun hat steht man manchmal vor der Aufgabe Zeilen nach IP-Nummern zu sortieren, und sei es nur um einen gewissen (dem durchschnittlichen Administrator aber fremden) Ordnungssinn zu befriedigen. Sehen wir uns mal als einfache Beispielmaterial meine /etc/hosts an:

127.0.0.1	localhost	localhost.localdomain

192.168.1.8	linux-1		linux-1.meine.domain
192.168.4.1	win-pc		win-pc.meine.domain
192.168.1.10	gw		gw.meine.domain

192.168.12.49	nameserver	nameserver.provider.domain
192.168.12.50	mailserver	mailserver.provider.domain

Da sind ein WinPC im 4er Netz, mein Linux Rechner und mein Router definiert. Ausserdem gibt's da noch zwei wichtige Rechner bei meinem Provider. Klar, die IP-Nummern sind gemogelt, aber das tut hier nichts zur Sache.

Sortieren wir die hosts mal auf die offensichtliche Weise:

# sort </etc/hosts | awk '/./'
127.0.0.1	localhost	localhost.localdomain
192.168.1.10	gw		gw.meine.domain
192.168.1.8	linux-1		linux-1.meine.domain
192.168.12.49	nameserver	nameserver.provider.domain
192.168.12.50	mailserver	mailserver.provider.domain
192.168.4.1	win-pc		win-pc.meine.domain

Euch ist sicherlich klar, das daß der Zusatz "awk '/./'" die Leerzeilen aus der Ausgabe filtert. Deswegen fehlen sie in der Ausgabe. Aber das nur am Rande. Was wirklich wichtig ist ist, daß die Zeilen zwar über das IP-Nummernfeld sortiert sind, der Feldinhalt selbst aber alphabetisch und nicht numerisch, speziell IP-numerisch, interpretiert wird. Das läuft dann darauf hinaus, daß die 192.168.1.8 hinter der 192.168.1.10 einsortiert wird. Ich weiss nicht wir ihr das seht, ich halte diese Reihenfolge für falsch, oder besser: es ist zumindest nicht das was ich will.

Aha, war also nix mit diesem Ansatz. Gut, wir können jetzt ein eigenes Sortierprogramm für IP-Nummern in awk schreiben. Sicherlich eine schöne Aufgabe. Aber halt, wie war das? sort sortiert alphabetisch. Da können wird vielleicht was machen:

# awk '/^[1-9]/ { \
	split($1, x, "."); \
	str = sprintf("%03d.%03d.%03d.%03d", x[1], x[2], x[3], x[4]); \
	print str, $0; \
	} </etc/hosts | sort
127.000.000.001 127.0.0.1	localhost	localhost.localdomain
192.168.001.008 192.168.1.8	linux-1		linux-1.meine.domain
192.168.001.010 192.168.1.10	gw		gw.meine.domain
192.168.004.001 192.168.4.1	win-pc		win-pc.meine.domain
192.168.012.049 192.168.12.49	nameserver	nameserver.provider.domain
192.168.012.050 192.168.12.50	mailserver	mailserver.provider.domain

Ah ja, in der ersten Spalte sieht man den Trick. Wir fuellen die vier einzelnen IP-Teilnummern mit fuehrenden Nullen auf damit sort seine Arbeit erledigen kann. Und diesmal macht sort die Sache richtig.

Ok, die Sache hat den Schönheitsfehler, daß die aufgefüllten IP-Nummern in unserer Ausgabe enthalten sind. Wenn ich vorhätte die Ausgabe in eine Tabellenkalkulation zu laden, um sie dort weiter zu verarbeiten würde ich sie wahrschleinlich drin lassen. Sie zu entfernen ist aber auch nicht das Problem. Die sort-Ausgabe wird in folgenden Filter geleitet:

... | awk '{ sub(/^[^ ]+ /, "", $0); print }'

Das löscht alles bis zum (und inklusive dem) ersten Leerzeichen und alles ist wieder schön.

Wenn ich öfter vor so einem Problem stünde (was nicht der Fall ist) würde ich mir jetzt Gedanken machen, wie ich sowas in ein kleines Skript packe.

Na gut, obwohl ich es eigentlich nicht brauche habe ich dazu was geschreiben:

#!/usr/bin/gawk -f
#

#
# ipsort [<fieldnum>|-]
#
#	Sort the input by the IP-number in the <fieldnum>-th field
#	or remove the temporarily added ip-sort-field from the input.
#

BEGIN {
	STDERR = "/dev/stderr";
	ipfield = ARGV[1];  ARGV[1] = "";

	if (ipfield == "-")
		remove = 1;
	else if (ipfield+0 <= 0) {
		printf ("ipsort: no or zero ip-number field\n") >STDERR;
		exit(1);
		}
	}

/./ {
	if (remove == 1) {
		sub(/^[^ ]+ /, "", $0);
		print $0;
		}
	else if (split($ipfield, x, ".") == 4) {
		str = sprintf("%03d.%03d.%03d.%03d", x[1], x[2], x[3], x[4]);
		print str, $0 | "sort";
		}
	}

ipsort erwartet als ersten Parameter das awk Feld in dem sich die IP-Nummer befinden soll. Nur wenn an dieser Stelle in der Eingabe tatsäschlich sowas wie eine IP-Nummer steht (die split()-Funktion muss 4 Felder zurückliefern) wird der Zeile die temporäre "normalisierte" IP-Nummer vorangestellt und an das sort-Kommando weitergegeben. Ist der Feldparameter - entfernt ipnum den temporären Eintrag. Mit ipsort können wir die hosts dann mit

# ipsort 1 /etc/hosts | ipsort -
127.0.0.1	localhost	localhost.localdomain
192.168.1.8	linux-1		linux-1.meine.domain
192.168.1.10	gw		gw.meine.domain
192.168.4.1	win-pc		win-pc.meine.domain
192.168.12.49	nameserver	nameserver.provider.domain
192.168.12.50	mailserver	mailserver.provider.domain

sortieren. Das sieht schon viel einfacher aus als das was wir zuerst hatten.

Wenn ihr euch das "| ipsort -" auf der Kommandozeile sparen wollt könnt ihr das Skript an einer Stelle ändern. Aus

	print str, $0 | "sort";

wird

	print str, $0 | "sort | ipsort -";

und das temporäre Feld wird automatisch entfernt.

< dag | at | awk-scripting.de >