Menüs und Masken in der Shell nutzen mit Dialog

© Malalena, sxc.hu

Mehr Komfort

Von einfachen Abfragen bis hin zu komplexen Menüs: Mit dem Toolkit Dialog bauen Sie eine grafische Oberfläche für Shell-Skripte, die oft nicht mehr als eine zusätzliche Zeile brauchen.

README

Für die Shell gibt es vorgefertigte Bausteine, die es erlauben, eine einfache Oberfläche mit Textfeldern, Listen und Auswahl in wenigen Zeilen zu programmieren. Damit ermöglichen Sie Anwendern ein komfortables Interface zu Ihrem Skript.

Eigentlich handelt es sich bei der Shell ja schon um eine Benutzerschnittstelle – aber kaum ein Nutzer fragt Daten noch auf Zeilenbasis ab. Um den Gewohnheiten der modernen Anwender Rechnung zu tragen, gibt es mit Dialog ein textbasiertes GUI-Toolkit, das die Elemente einer grafischen Oberfläche nachbildet.

Die Optik wirkt etwas antiquiert, aber in Sachen Schnelligkeit ist die Technik kaum zu übertreffen. Läuft auf dem System ein X-Server, verschaffen Sie sich noch etwas mehr Komfort, wenn Sie auf grafische Dialog-Pendants wie beispielsweise Zenity oder Gtkdialog setzen (siehe Kasten "Verwandte").

Verwandte

Neben dem hier gezeigten Dialog existieren weitere Projekte mit gleichem Zweck. In der Tabelle "Alternativen" finden Sie heraus, in welchem Umfang die anderen Kandidaten die Fenstertypen anbieten. Einige der Alternativen benötigen eine grafische Benutzeroberfläche: Dazu gehören Xdialog [3], Zenity [4], Kdialog [5] und Gtkdialog [6]. Letzteres weicht vom üblichen Muster ab: Hier erstellen Sie XML-Dateien mit den Anweisungen, die das Programm beim Aufruf einliest.

Alternativen

Dialog-Befehl

Xdialog

Zenity

Kdialog

--yesno

--yesno

--question

--yesno

--msgbox

--msgbox

--warning oder --info

--msgbox

--infobox

--infobox

--passivepopup

--textbox

--textbox

--textinfo

--textbox

--tailbox

--tailbox

--pause

--gauge

--gauge

--progress

--progressbar

--form

--inputmenu

--2inputsbox/--3inputsbox

--calendar

--calendar

--calendar

--calendar

--timebox

--timebox

--inputbox

--inputbox

--entry

--inputbox

--editbox

--editbox

--textinfo

--textinputbox

--dselect

--dselect

--file-selection

--getexistingdirectory

--fselect

--fselect

--file-selection

--getopenfilename/--getsavefilename

--checklist

--checklist

--list

--checklist

--radiolist

--radiolist

--list

--radiolist

--menu

--menubox

-- list

--menu

Masken und Menüs

Möchten Sie Daten vom Benutzer abfragen, bietet sich bei einfachen Zeichenketten der in die Bash eingebaute Befehl read an, der oft in Kombination mit dem Kommando echo zum Einsatz kommt. Möchten Sie den Prompt für die Eingabe abändern, geben Sie über die Option -p einen neuen Text vor (Listing 1).

Über die Option -n (Zeile 4) verhindern Sie das Newline am Ende der Echo-Anweisung. Daher erscheint der Text direkt rechts vom Read-Prompt. Anführungszeichen in der Ausgabe maskieren Sie mittels eines Rückstrichs ("Backslash"), damit diese keinen Einfluss auf das Ergebnis haben. Die beiden Methoden aus Zeile 4 und Zeile 7 erzeugen das gleiche Ergebnis.

Listing 1

 

#! /bin/sh
echo "---------------------------"
echo "echo -n \"Eingabe: \";read a"
echo -n "Eingabe: ";read a
echo "---------------------------"
echo "read -p \"Eingabe: \" a"
read -p "Eingabe: " a
echo "---------------------------"

Mit der Option -i Text in Kombination mit -e (Readline-Support) geben Sie dem Benutzer schon eine Eingabe am Prompt vor. Etwas komfortabler klappt das mit dem externen Programm Readpreprompt [1]. Für den korrekten Einsatz führen Sie das Kommando in einer Subshell aus (Listing 2, Zeile 3). Das Tool liefert das Ergebnis der Abfrage auf die Standardausgabe. Mit diesem Befehl gestalten Sie leicht Masken für Datenbankanwendungen.

Listing 2

 

#! /bin/sh
a="Alter Wert"
a=$(readpreprompt "Eingabe: " "$a")
echo $a

Der eingebaute Befehl echo bietet nur wenig Einfluss auf das Format der Ausgabe. Um Zahlenwerte exakt positioniert auf den Bildschirm zu schreiben, hilft Ihnen das der Programmiersprache C entliehene printf. Bei der Ausgabe von Zahlenwerten mit Nachkommastellen nimmt Ihnen der Befehl das Auf- oder Abrunden ab, zudem berücksichtigt er die Spracheinstellungen der Shell.

Der grundsätzliche Aufbau von Printf-Kommandos folgt dem Aufbau printf "%Format" Daten. Die wichtigsten Anweisungen zum Formatieren finden Sie in der Tabelle "Mit Format". Der Befehl unterscheidet zwischen Komma und Punkt als Dezimaltrenner. Im Ernstfall passen Sie die Variable LC_NUMERIC oder LANG mittels set und unset innerhalb des Skripts an.

Mit Format

Beispiel

Erläuterung

%5.2f

Fließkommazahl mit fünf Stellen vor und zwei nach dem Trenner

%.10s

Zeichenkette mit maximal zehn Zeichen Breite

%X\n

Hexadezimalzahl mit Großbuchstaben

%x\n

Hexadezimalzahl mit Kleinbuchstaben

%#X\n

Hexadezimalzahl mit Großbuchstaben und führendem 0X

%i\n

Ganzzahl (Integer)

Einfacher bringen Sie jedoch mittels tr die Eingabedaten gleich auf das richtige Format. Dies integrieren Sie bei Bedarf direkt in die Anweisung (Listing 3, Beispiel 2). Die Anweisungen für Printf schließen Sie immer mit einem Newline (\n) ab. In einigen Fällen erlaubt der Befehl auch Tabulatoren. Die Beispiele in Listing 3 zeigen die wichtigsten Funktionen, Abbildung 1 die Ergebnisse.

Listing 3

 

#! /bin/bash
# Beispielwerte
a=987,455
b=987.455
c="Das-ist-ein-langes-Wort"
d=30
# Beispiel 1
# Ausgabe Fließkommazahl, Rundung auf 2 Stellen
printf "%5.2f\n" $b
# Beispiel 2
# Ausgabe Fließkommazahl mit "falschem" Dezimaltrenner
printf "%5.2f\n" `echo $a | tr , . `
# Beispiel 3
# Textausgabe auf zehn Zeichen gekürzt
printf "%.10s\n" $c
# Beispiel 4
# Numerische Umwandlungen bei Darstellung:
# Ganzzahl, Hexadezimalzahl, Oktalzahl
printf "%i %X %o\n" $d $d $d
# Beispiel 5
# Numerische Ausgabe Ganzzahl,
# Hexadezimalzahl (Kleinbuchstaben) mit führenden "0x"
printf "%i  %#x\n " $d $d
Abbildung 1: Über Parameter formatieren Sie mittels printf die Ausgaben von Daten in der gewünschten Form.

Beachten Sie eine Besonderheit bei der Ausgabe von Zeichenketten (Strings): Das Leerzeichen dient normalerweise als Trenner. Das macht jedes Wort einer Zeile zu einem eigenen Variablenwert. In Listing 3 erhielten Sie also eine fünfzeilige Ausgabe, stünden im String der Variablen c Leerzeichen anstelle der Bindestriche.

Beim Aufbau von Menüs gehen Sie wie in Listing 4 vor. Besteht die Auswahlliste nur aus wenigen Punkten, verwenden Sie Ziffern zu deren Kennzeichnung. Damit steuern Sie das Skript komfortabel über den Ziffernblock. Gehen Ihnen die Zahlen aus, verwenden Sie stattdessen Kleinbuchstaben. Bei den Test-Anweisungen behandelt das Skript die Funktionsvariable als String: Das vermeidet verwirrende Fehlermeldungen bei der Eingabe von unpassenden Zeichen. Abbildung 2 zeigt das Beispiel in Aktion.

Listing 4

 

#! /bin/sh
while true; do
  clear
  echo "(1) Funktion A"
  echo "(2) Funktion B"
  echo "(9) Ende"
  echo " "
  echo -n "Funktion auswählen: "; read f
  if [ "$f" = "1" ]; then
    echo "FUNKTION A";sleep 3
  elif [ "$f" = "2" ]; then
    echo "Funktion B";sleep 3
  elif [ "$f" = "9" ]; then
    exit
  fi
done
Abbildung 2: Ein einfaches Menü, das Sie über die vorangestellten Ziffern steuern.

Das Beispiel aus Listing 5 (Abbildung 3) zeigt eine Maske zum Bearbeiten von Daten für eine Adressverwaltung. Für den praktischen Einsatz wären noch die Funktionen zum Holen und Sichern der Daten in einer Datenbank notwendig.

Listing 5

 

#! /bin/sh
# Belegung Beispieldaten
# An diese Stelle würde ein Datenbankzugriff integriert
a="Herr"
b="Pano Garçon"
c="Testsystem 123"
d="90003 Nürnberg"
while true; do
  clear
  echo "-------------------------------------------"
  echo "      Adressbearbeitung"
  echo "------+----------+-------------------------"
  echo "F-Nr. |          |    Wert"
  echo "  1   |  Anrede: | "$a
  echo "  2   |    Name: | "$b
  echo "  3   |  Straße: | "$c
  echo "  4   | PLZ/Ort: | "$d
  echo "------+----------+-------------------------"
  echo "Aktionen: [F-Nr]: Zeile ändern, [s] speichern,"
  echo "[q] Abbruch"
  echo -n "Aktion: ";read wn
  if [ "$wn" = "1" ]; then
    a=$(readpreprompt "Zeile $wn: " "$a")
  elif [ "$wn" = "2" ]; then
    b=$(readpreprompt "Zeile $wn: " "$b")
  elif [ "$wn" = "3" ]; then
    c=$(readpreprompt "Zeile $wn: " "$c")
  elif [ "$wn" = "4" ]; then
    d=$(readpreprompt "Zeile $wn: " "$d")
  elif [ "$wn" = "s" ]; then
    echo "Hier würde in die Datenbank geschrieben werden"
    break
  elif [ "$wn" = "q" ]; then
    exit
  fi
done
echo "-----------------------------"
echo $a
echo $b
echo $c
echo $d
Abbildung 3: Mit wenigen Zeilen Shell-Code haben Sie eine einfach Maske zum Bearbeiten von Daten aus einer Datenbank erstellt.

Falls Sie PostgreSQL den Shell-Client psql nutzen, löschen Sie das bei der Datenabholung und Variablenbelegung enthaltene führende Leerzeichen. Mittels cut weisen Sie den Wert erst ab dem zweiten Byte der Variablen zu:

b=$(psql -t -c "select name from adressen where orgnr = 1;" | cut -b 2-)

Im Beispiel sprechen Sie die Datenzeilen mit Nummern und die Funktionen mit Buchstaben an. Nach jeder Änderung baut das Skript den Bildschirm neu auf.

Mit Dialog

Das Programm Dialog [2] liegt vielen aktuellen Distributionen bei. Die Anweisungen setzen sich aus Befehlen zum Gestalten der Fenster, für den Typ und aus geometrischen Anweisungen zusammen. Am Anfang der Anweisung zum Gestalten geben Sie den Titel und einen Hintergrundtitel an. Ferner erwarten die Befehl am Ende zwingend Angaben zur Geometrie.

Die Zeiten des Kästchenzählens sind aber dank Dialog vorbei: Wollen Sie keine Angaben für Höhe (Zeilen) und Breite (Zeichen) machen, geben Sie einfach 0 0 an. Das klappt bei den meisten Anweisungen. Das Tool passt die Proportionen dann automatisch an. Listing 6 zeigt den Aufbau des Befehls.

Listing 6

 

$ dialog --title "Titel" --backtitle "Hintergrundtitel" Weitere Anweisungen 0 0

In den Anweisungen zum Fenstertyp geben Sie an, was in dieser Codezeile passiert. Möchten Sie, dass der Benutzer etwas liest, eingibt oder entscheidet? In der Tabelle "Fenstertypen" finden Sie eine Auswahl an Möglichkeiten.

Fenstertypen

Anweisung

Syntax

Rückgabewert

Darstellung

Ja/Nein-Frage

--yesno "Text"

Ja: 0, Nein: 1

Nachrichtenbox

--msgbox "Text"

0

Infobox

--infobox "Text"

-

Textbox

--textbox Dateiname Höhe Breite

0)

Ende einer Datei anzeigen

--tailbox Dateiname Höhe Breite

-

Pause

--pause "Text" Höhe Breite Sekunden

-

Fortschrittsanzeige

--gauge "Text" 0 0

-

Eingabemenü

--inputmenu "Text" Höhe Breite Menühöhe "Menüpunkt1" "Wert1" ...

Daten auf die Standardausgabe

Kalender

--calendar "Text" 0 0

Datum

Zeitwerte erfassen

--timebox "TEXT" 0 0

Zeitangabe

Eingabebox

--inputbox "Text" 0 0 "Vorgabe

alphanumerische Werte

Mini-Editor

--editbox Eingabedatei Höhe Breite > Ausgabedatei

Textdatei

Verzeichnis auswählen

--dselect Verzeichnis 0 0

Verzeichnisname

Datei auswählen

--fselect Verzeichnis Höhe Breite

Dateiname mit Pfad

Während die Ja/Nein-Frage und die Nachrichtenbox stets auf eine Eingabe warten, dient die Infobox zur reinen Ausgabe. Im Zusammenhang mit sleep zeigen Sie so Nachrichten für eine bestimmte Zeit an. Anstelle von sleep ließen sich natürlich andere Programme einbinden.

Sie haben die Möglichkeit, die Ja/Nein-Frage um je eine Schaltfläche zu erweitern. Eine Schaltfläche mit eigener Beschriftung erhalten Sie mit folgenden zusätzlichen Parametern:

--extra-button --extra-label "Text"

Beim Betätigen erhalten Sie den Rückgabewert 3. Die zweite Erweiterungsmöglichkeit besteht mit der Hilfe-Fläche --help-button. Sie benötigen hier keine weiteren Angaben zur Beschriftung, der Rückgabewert beträgt 2. Zusammen mit Ja (0) und Nein (1) erzeugt das Widget also vier Rückgabewerte.

Das kleine Skript aus Listing 7 zeigt die Wirkungsweise. Sie sehen darin, wie Sie eine Schaltfläche abweichend beschriften oder belegen. Der Befehl im ersten Aufruf ist entsprechend ergänzt. Sie finden vor dem Parameter --yesno den zusätzlichen Parameter --ok-label "Text".

Listing 7

 

#! /bin/sh
while true; do
  dialog --title "Titel" --backtitle "Hintergrundtitel" --help-button --extra-button --extra-label "EXTRA"  --ok-label "Zustimmung" --yesno "FRAGETEXT" 0 0
  dialog --title "Titel" --backtitle "Hintergrundtitel" --msgbox "Rückgabewert: $?" 0 0
  dialog --title "Titel" --backtitle "Hintergrundtitel" --defaultno --yesno "Shellskript beenden?"  0 0
  if [ $? -eq 0 ]; then
    exit
  fi
done

Gleiches gilt analog für die Nein-Schaltfläche (--no-label "Text"), die Ja-Schaltfläche (--yes-label "Text") und die Hilfe (--help-label "Text"). Mit dem vorangestellten --defaultno in der letzten dialog-Anweisung steht die Auswahl nach dem Start auf No.

Bei der Fortschrittsanzeige übermitteln Sie den Prozentwert per Pipe auf das Dialog-Kommando (Listing 8). Das Element eignet sich ebenfalls zur Anzeige von Anteilen an einem Gesamtwert.

Listing 8

 

#! /bin/sh
prozent=0
while [ $prozent -lt 100 ]; do
  # Erzeugen der Prozent-Angabe
  prozent=$(echo "$prozent + 10" | bc)
  # Der Wert für gauge wird über die Pipe an dialog übergeben
  echo $prozent | dialog --title "Titel" --backtitle "Hintergrundtitel" --gauge "FORTSCHRITTSTEXT" 0 0
  # sleep nur für Demo!
  sleep 1
done

Listing 9 ist "gebrauchsfertig": Es zeigt die Belegung der Platte durch das Verzeichnis /home an (Abbildung 4). Unübersichtlich gestaltet sich hier das Herauslösen der Werte aus der df-Abfrage. Der Prozentwert erscheint als Balken, der noch freie Speicherplatz im Klartext.

Listing 9

 

#! /bin/sh
# Prozentuale Belegung aus df -h übergeben
PROZENT=$(df -h | grep "/home" | tr -s ' ' | cut -d' '  -f5 | cut -d% -f1)
FREI=$(df -h | grep "/home" | tr -s ' ' | cut -d' ' -f4)
echo ${PROZENT} | dialog --title "Plattenbelegung von /home " --backtitle "Systemauskunft" --gauge "\n Aktuell freier Plattenplatz: ${FREI}B" 10 50
sleep 5
Abbildung 4: Mit einem Blick sehen Sie, wie voll die Home-Partition gerade ist.

Raspberry Pi Geek kaufen

Einzelne Ausgabe
 
Abonnements
 
TABLET & SMARTPHONE APPS
Bald erhältlich
Get it on Google Play

Deutschland

Ähnliche Artikel

Aktuelle Ausgabe

08/2019
Smarte Hacks

Diese Ausgabe als PDF kaufen

Preis € 9,99
(inkl. 19% MwSt.)

Stellenmarkt

Neuigkeiten

  • Immer der Reihe nach

    Mit einer einfachen Schaltung testen Sie Bausteine, die über den SPI-Bus miteinander kommunizieren.

  • Einzigartig

    Eine MAC-Adresse ermöglicht die Kommunikation im Netzwerk. Der 24AA02E48 liefert sie für Setups, die aus grundlegenden Komponenten bestehen.

  • Steinkuchen

    Der Raspberry Pi hat viele Fans, doch nicht ohne Grund sehnen sich viele Anwender nach einem RasPi 4. Der Rock Pi 4 übertrumpft den RasPi 3 in Sachen Leistung um Längen und schlägt sich auch in der Praxis gut.

  • Kerngeschäft

    Der Einstieg in die Welt der Mikrocontroller fällt nicht ganz leicht. Mit dem Nucleo F401RE haben Sie jedoch ein Entwicklerboard samt abgestimmter Entwicklungsumgebung an der Hand.

  • Himbeer-Geräte

    Mit Maus und Tastatur im weiß-roten Raspberry-Look macht die Raspberry Pi Foundation das eigene Angebot an Peripheriegeräten für den Mini-Rechner komplett.

  • Unter Kontrolle

    PiCockpit ist eine speziell auf den RasPi zugeschnittene Online-Monitoring-Lösung. Wir werfen einen ersten Blick auf den brandneuen und in Ausbau befindlichen kostenlosen Dienst.

  • Ins rechte Licht gesetzt

    Selbst ohne eigenen Garten holen Sie sich Pflanzen mithilfe von LEDs in jeden Raum und sehen ihnen mit dem RasPi beim Wachsen zu.

  • Helligkeit nach Maß

    Wer bei wechselnden Lichtverhältnissen nicht ständig die Beleuchtung manuell nachregeln möchte, der spannt dafür einen PiXtend ein.

  • Geschrumpft

    Kleine Bildschirme gibt es zwar viele, aber der Support von Raspbian ist überraschend schlecht. Mit ein paar Tricks erzielen Sie trotzdem ein optimales Ergebnis.

  • Brüllwürfel

    Kompakt-Stereoanlagen mit CD-Spieler und Kassettendeck sind inzwischen oft ein Fall für den Recyclinghof – oder für die digitale Wiederbelebung mit einem RasPi und etwas Geschick.