Da ich immer wieder sehr viel ausprobiere und diverse Services in meinem lokalen Netzwerk zur Verfügung stelle, muss ich auch immer wieder per SSH auf die entsprechenden Hosts zugreifen. Der Nameserver in meinem Netzwerk stellt dabei alle Services als Subdomain von .hofmann.lan zur Verfügung. Das heißt, wenn ich jetzt zum Beispiel auf meine phpmyadmin-Installation zugreifen will, muss ich folgendes SSH Kommando ausführen:

ssh phpmyadmin.hofmann.lan

Verdammt viel zu tippen, wenn man so tipfaul ist, wie ich.

Bisher hab ich dazu immer Aliases angelegt, so dass ich irgendein Kürzel definiert habe, um schnell auf irgendwas zuzugreifen. Aber das ist weder dynamisch noch besonders einfallsreich.

Voraussetzungen

Ich habe mal vor einiger Zeit einen Webservice gebastelt, der von meinem Nameserver das Zone-File ausliest und die darin enthaltenen Einträge als JSON-Liste zurück gibt. Die Liste sieht ungefähr so aus:

[
    {
        "ipaddress":"192.168.0.5",
        "subdomain":"odroid.hofmann.lan"
    },
    {
        "ipaddress":"192.168.0.11",
        "subdomain":"desktop.hofmann.lan"
    },
    {
        "ipaddress":"192.168.0.12",
        "subdomain":"laptop.hofmann.lan"
    }
]

Diese Liste kann man dafür hervorragend verwenden. Ich will jetzt aber gar nicht darauf eingehen, wie ich den Webservice aufgebaut habe, sondern ich will zeigen, wie man diese Daten verwenden kann.

Daten aufbereiten für ein Bash-Completion-Script

Mein erster Gedanke war, wie ich die Daten in ein Format bringen kann, das ich verwenden kann. Zuerst braucht man natürlich einen Zugriff auf den Webservice. Das kann man in Bash ganz leicht mit curl machen:

curl -s dns-sniffer.hofmann.lan/api/dns/get/hofmann.lan

Dieses Kommando liefert mir schonmal das gesamte JSON, das ich benötige. Ich brauche aber davon lediglich die Werte im Feld subdomain. Hier musste ich dann schon ein bisschen suchen, weil JSON nicht so einfach in der Bash verarbeitet werden kann. Ich könnte natürlich den Webservice entsprechend anpassen, dass mir nur noch die Subdomains als plain text geliefert werden, aber es gibt zum Glück auch andere Möglichkeiten.
Das Kommando (oder Progrämmchen) zum Verarbeiten von JSON nennt sich jq (json query). Die Syntax für meinen Anwendungsfall schaut so aus:

jq -r '.[] | .subdomain'

Ganz grob übersetzt bedeutet das, dass ich aus jedem Element des Arrays das Feld subdomain haben will. Der Parameter -r gibt an, dass ich das Ganze raw haben will, also ohne Quotes um die einzelnen Ergebnisse.

Diese beiden Kommandos kann man jetzt noch kombinieren und das Ergebnis sieht dann so aus:

curl -s dns-sniffer.hofmann.lan/api/dns/get/hofmann.lan|jq -r '.[] | .subdomain'

Und es wird jetzt folgende Liste ausgegeben:

odroid.hofmann.lan
desktop.hofmann.lan
laptop.hofmann.lan

Das Script für die Bash-Completion erstellen

Die Scripts für die Vervollständigung findet man im Verzeichnis /etc/bash_completion.d. Hier legt man sich ein neues Script mit dem Namen ssh an und kopiert folgenden Inhalt hinein:

function _ssh
{
    local cur prev opts
    COMPREPLY=()
    cur="${COMP_WORDS[COMP_CWORD]}"
    prev="${COMP_WORDS[COMP_CWORD-1]}"
    opts=$(curl -s dns-sniffer.hofmann.lan/api/dns/get/hofmann.lan|jq -r '.[] | .subdomain')

    COMPREPLY=( $(compgen -W "$opts" -- ${cur}) )
    return 0
}
complete -F _ssh ssh

Ich gehe jetzt nicht auf jede Zeile ein, da man das alles aus der Doku rauslesen kann. Im Grunde sehen die Files ohnehin immer gleich aus. Lediglich die Zeile opts=… enthält die für uns wichtigen Informationen. Hier schreibt man nämlich das Kommando rein, das wir vorher erarbeitet haben. Das Ganze soll in einer Variablen angelegt sein.
Die letzte Zeile legt dann fest, dass beim Kommando ssh die Funktion _ssh für die Parameter verwendet werden soll und diese liefert die entsprechenden Parameter die in opts festgelegt wurden.
Anschließend einmal neu am System anmelden bzw. falls man auf einem Desktop arbeitet, reicht es auch aus, wenn man das Terminal neu öffnet und das war es dann auch schon.

Fazit

Jetzt muss ich nur immer einen Eintrag im Nameserver erzeugen, wenn ich einen neuen Dienst im Netzwerk bereitstelle. Eine weitere Konfiguration auf den Clients ist nicht mehr notwendig und ich kann immer auf alle meine Hosts mit wenig Tippen zugreifen.

ssh o<tab>

Das reicht, um den Text auf ssh odroid.hofmann.lan zu vervollständigen.