Portabel programmieren mit Dialog, Gdialog und Kdialog

Dialogregie

Bash-Skripte sind meist nur für die Verwendung auf der Kommandozeile gedacht und schrecken Nicht-Experten mit unübersichtlichen Ausgaben ab. Das muss aber nicht so sein: Die Programme Dialog, Gdialog und Kdialog verstecken die Kommandozeile vollständig vor dem Benutzer.

Bash-Skripte erledigen in der Regel nur Kleinigkeiten. Administratoren und Programmierer benutzen die Bash praktisch ausschließllich, um alltägliche Standardaufgaben zu automatisieren, für die es sich nicht lohnt, ein ausgewachsenes Binärprogramm in einer Hochsprache zu schreiben.

Ein großes Manko fast aller Skripte stellen das rudimentäre Benutzer-Interface und die meist schlechte Benutzerführung dar. Der von KDE und Gnome verwöhnte Anwender findet es umständlich, erst eine Textkonsole zu öffnen und dann knappe Fragen mit Tastatureingaben zu beantworten. Die Kommandozeile schreckt insbesondere Einsteiger ab, die mit den Linux-Befehlen noch nicht vertraut sind.

Standardisierte Menüs

Das Programmdialog ermöglicht die Darstellung eines ansprechenderen Benutzer-Interfaces. Dialog war anfangs nur für die Kommandozeile gedacht und baute aus farbigen Hintergründen und einigen ASCII-Zeichen Rahmen und Menüs. Mit der zunehmenden Verbreitung von KDE und Gnome hat auch Dialog nachgezogen und verfügt mittlerweile über Ableger gebildet: kdialog für KDE und gdialog für Gnome.

Dialog und seine grafischen Pendants werden komplett über die Kommandozeile gesteuert. Genügen bei einfachen Abfragen noch eine Hand voll Parameter, gestalten sich große Auswahllisten oder Menüs schnell unübersichtlich. Dialog baut dann selbständig anhand der Kommandozeilen-Parameter die Bedienoberfläche auf und übernimmt die komplette Interaktion mit dem Benutzer.

Bei Menüs oder Abfragen liefert Dialog die Auswahl des Anwenders an das aufrufende Programm oder Skript zurück. Beim Ur-Dialog für die Kommandozeile muss dies über den Rückgabewert von Dialog oder die Standard-Fehlerausgabe (stderr) laufen. Die Standardausgabe (stdout) benötigt Dialog für die Interaktion mit dem Benutzer.

Grafische Oberfläche gefällig?

Bei den grafischen Varianten Gdialog und Kdialog wäre dies prinzipiell nicht erforderlich: Sie könnten die Auswahlen per Standard-Ausgabe zurück geben. Aus Kompatibilitätsgründen zu Dialog verwendet Gdialog jedoch weiterhin die Standard-Fehlerausgabe. Leider verwendet Kdialog statt dessen die Standardausgabe, was eine portable Programmierung erschwert.

Als Beispiel soll der Termin-Client eines Terminüberwachungs-Skripts dienen, das bereits im siebten Teil der Programming Corner aus LinuxUser 02/2002 [1] vorgestellt wurde. Der Client kommuniziert über Dialog, Gdialog oder Kdialog mit dem Benutzer. Währenddessen läuft der Server still im Hintergrund. Listing 1 enthält den um einige Kommentare verkürzten Quellcode des Termin-Clients.

Listing 1

Der Termin-Client

001  #!/bin/bash
002  if [ -n "`which gdialog 2>/dev/null`" ]; then
003    Dialog="`which gdialog`"
004    DialogRedirect="2>~/.termine.in"
005  elif [ -n "`which kdialog 2>/dev/null`" ]; then
006    Dialog="`which kdialog`"
007    DialogRedirect="1>~/.termine.in"
008  elif [ -n "`which dialog 2>/dev/null`" ]; then
009    Dialog="`which dialog`"
010    DialogRedirect="2>~/.termine.in"
011  else
012    echo "Sie benötigen dialog, kdialog oder gdialog,"
013    echo "um dieses Programm benutzen zu können."
014    exit 1
015  fi
016
017  COLUMNS=80
018  LINES=24
019
020  AuswahlMenue ()
021  {
022    Titel="\"$1\""
023    Tasten="$2"
024    Namen="$3"
025
026    IFS=$'\t'
027    set – $Tasten
028    i=1
029    while [ "$#" -gt "0" ]; do
030      Taste[$i]="\"$1\""
031      shift
032      i=$[$i+1]
033    done
034    Elemente="$i"
035
036    IFS=$'\t'
037    set – $Namen
038    i=1
039    while [ "$#" -gt "0" ]; do
040      Name[$i]="\"$1\""
041      shift
042      i=$[$i+1]
043    done
044
045    Menu=""
046    for ((i=1; $i<$Elemente; i=$[$i+1])); do
047      Menu="${Menu}${Taste[$i]}${IFS}${Name[$i]}${IFS}"
048    done
049
050    Overhead="7"
051    if [ "$[$Elemente+$Overhead]" -lt "$LINES" ]; then
052      DisplayLines="$[$Elemente+$Overhead]"
053    else
054      DisplayLines="$LINES"
055    fi
056    DialogParms="--menu $Titel $DisplayLines $[$COLUMNS-8] $[$Elemente-1] $Menu"
057    eval $Dialog $DialogParms $DialogRedirect
058    read < ~/.termine.in
059  }
060  TextEingabe ()
061  {
062    DialogParms="--inputbox \"$1\" 8 $[$COLUMNS-8] \"$2\""
063    eval $Dialog $DialogParms $DialogRedirect
064    read < ~/.termine.in
065    if [ "$REPLY" = "" ]; then
066      REPLY="$2"
067    fi
068  }
069  JaNein ()
070  {
071    DialogParms="--yesno \"$1\" 5 $[$COLUMNS-8]"
072    eval $Dialog $DialogParms $DialogRedirect
073  }
074  Fehler ()
075  {
076    DialogParms="--msgbox \"$1\" 5 $[$COLUMNS-8]"
077    eval $Dialog $DialogParms $DialogRedirect
078  }
079  TerminBearbeiten ()
080  {
081    Nr="$1"
082
083    while true; do
084      Keys="d${IFS}v${IFS}b${IFS}t${IFS}c${IFS}l${IFS}z${IFS}"
085      Menu="Datum: `date -d ${Datum[$Nr]} +%d.%m.%Y`${IFS}Vorwarnung: ${Vorwarnzeit[$Nr]} Tage${IFS}Betreff: ${Bezeichnung[$Nr]}${IFS}Text: ${Text[$Nr]}${IFS}CCs: ${CCs[$Nr]}${IFS}Löschen${IFS}Zurück"
086
087      AuswahlMenue "Termin bearbeiten:" "$Keys" "$Menu"
088      Auswahl="$REPLY"
089
090      case $Auswahl in
091        z|"")
092          break
093          ;;
094        l)
095          JaNein "Termin löschen: Sind Sie sicher?"
096
097          if [ "$?" = "0" ]; then
098            Termine=$[$Termine-1]
099
100            for ((i=$Nr; $i < $Termine; i=$[$i+1])); do
101              Datum[$i]=${Datum[$[$i+1]]}
102              Vorwarnzeit[$i]=${Vorwarnzeit[$[$i+1]]}
103              Bezeichnung[$i]=${Bezeichnung[$[$i+1]]}
104              Text[$i]=${Text[$[$i+1]]}
105              CCs[$i]=${CCs[$[$i+1]]}
106            done
107          fi
108          break
109          ;;
110        d)
111          TextEingabe "Datum, in der Form yyyy-mm-dd:" "`date -d ${Datum[$Nr]} +%Y-%m-%d`"
112          Datum[$Nr]="`date -d $REPLY +%Y%m%d`"
113          ;;
114        v)
115          TextEingabe "Vorwarnzeit in Tagen:" "${Vorwarnzeit[$Nr]}"
116          Vorwarnzeit[$Nr]="$REPLY"
117          ;;
118        b)
119          TextEingabe "Kurze Bezeichnung des Termins:" "${Bezeichnung[$Nr]}"
120          Bezeichnung[$Nr]="$REPLY"
121          ;;
122        t)
123          TextEingabe "Ausführliche Beschreibung, Anmerkungen:" "${Text[$Nr]}"
124          Text[$Nr]="$REPLY"
125          ;;
126        c)
127          TextEingabe "Zusätzliche Empfänger der Warnung (Komma-Trennung):" "${CCs[$Nr]}"
128          CCs[$Nr]="$REPLY"
129          ;;
130      esac
131    done
132  }
133
134  ##### Hauptprogramm #####
135
136  if [ -r ~/.termine ]; then
137    IFS=$'\r'
138    read -a TerminListe << EOF
139  `grep -v "^#" ~/.termine | tr "\n" "$IFS"`
140  EOF
141
142    IFS=$'\t'
143    i=1
144    while [ "${TerminListe[$i]}" != "" ]; do
145      set – ${TerminListe[$i]}
146      Datum[$i]=$1
147      Vorwarnzeit[$i]=$2
148      Bezeichnung[$i]=$3
149      Text[$i]=$4
150      CCs[$i]=$5
151      i=$[$i+1]
152    done
153    Termine="$i"
154  else
155    MailAdresse="${LOGNAME}@localhost"
156    Termine=1
157  fi
158
159  while true; do
160    IFS=$'\t'
161    Keys="0${IFS}\$Keys${IFS}n${IFS}q${IFS}x${IFS}"
162    Menu="Adresse:  ${MailAdresse}\$Menu${IFS}Neuer Termin${IFS}Speichern und Ende${IFS}Ende ohne Speichern"
163    for ((i=1; $i < $Termine; i=$[$i+1])); do
164      Keys="${Keys/\$Keys/${IFS}$i\$Keys}"
165      Menu="${Menu/\$Menu/${IFS}`date -d ${Datum[$i]} +%d.%m.%Y`: ${Bezeichnung[$i]}\$Menu}"
166    done
167    Keys="${Keys/\$Keys}"
168    Menu="${Menu/\$Menu}"
169
170    AuswahlMenue "Hauptmenü" "$Keys" "$Menu"
171    Auswahl="$REPLY"
172
173    case $Auswahl in
174      q)
175        if [ ! -e ~/.termine ]; then
176          touch ~/.termine 2>/dev/null
177        fi
178        if [ -w ~/.termine ]; then
179          echo "$MailAdresse" > ~/.termine
180          IFS=$'\t'
181          for ((i=1; $i < $Termine; i=$[$i+1])); do
182            cat >> ~/.termine << EOF
183  ${Datum[$i]}${IFS}${Vorwarnzeit[$i]}${IFS}${Bezeichnung[$i]}${IFS}${Text[$i]}${IFS}${CCs[$i]}
184  EOF
185          done
186          break
187        else
188          Fehler "Die Termine-Datei (~/.termine) lässt sich nicht schreiben."
189        fi
190        ;;
191      x|"")
192        exit 1
193        ;;
194      n)
195        Datum[$Termine]="`date +%Y%m%d`"
196        Vorwarnzeit[$Termine]="2"
197        Bezeichnung[$Termine]=""
198        Text[$Termine]=""
199        CCs[$Termine]=""
200        Termine=$[$Termine+1]
201        TerminBearbeiten $[$Termine-1]
202        ;;
203      0)
204        TextEingabe "Ihre Email-Adresse:" "$MailAdresse"
205        MailAdresse="$REPLY"
206        ;;
207      *)
208        TerminBearbeiten $Auswahl
209        ;;
210    esac
211  done

LinuxCommunity kaufen

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

Deutschland

Ähnliche Artikel

  • Teil 7: Benutzerfreundliche Ein-/Ausgabe
    Im letzten Teil des Bash-Programmierkurses geht es um die benutzerfreundliche Gestaltung von Eingaben und Dialogen am Beispiel der Programme dialog, gdialog und kdialog.
  • Shell-Skripte mit grafischen Dialogen
    Dass sich Shell-Programmierung und grafische Oberflächen nicht ausschließen, zeigt KDialog: Nie war es leichter, Shell-Skripte um User-Interaktion zu ergänzen.
  • Textbasierte User-Interfaces (Teil 1)
    Die Kommandozeile halten viele Anwender für trist und schlecht bedienbar. Das muss aber nicht so sein: Mithilfe von Dialog und Whiptail werten Sie Ihre eigenen Skripts unkompliziert mit Fenstern, Menüs und Dialogen auf.
  • Klacker, Klacker, Klick, Klick
    Häufig erledigen Skripte ihre Aufgaben unsichtbar im Hintergrund. Mancher User wünscht sich aber ein visuelles Feedback. Dank Zenity und KDialog fügen sich Ihre Skripte nativ in die KDE- oder Gnome-Umgebung ein.
  • 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.
Kommentare

Infos zur Publikation

LU 05/2017: Linux unterwegs

Digitale Ausgabe: Preis € 5,95
(inkl. 19% MwSt.)

LinuxUser erscheint monatlich und kostet 5,95 Euro (mit DVD 8,50 Euro). Weitere Infos zum Heft finden Sie auf der Homepage.

Das Jahresabo kostet ab 86,70 Euro. Details dazu finden Sie im Computec-Shop. Im Probeabo erhalten Sie zudem drei Ausgaben zum reduzierten Preis.

Bei Google Play finden Sie digitale Ausgaben für Tablet & Smartphone.

HINWEIS ZU PAYPAL: Die Zahlung ist ohne eigenes Paypal-Konto ganz einfach per Kreditkarte oder Lastschrift möglich!

Aktuelle Fragen

Linux open suse 2,8
Wolfgang Gerhard Zeidler, 18.04.2017 09:17, 2 Antworten
Hallo.bitte um Hilfe bei. Code fuer den Rescue-login open suse2.8 Mfg Yvo
grep und sed , gleicher Regulärer Ausdruck , sed mit falschem Ergebnis.
Josef Federl, 15.04.2017 00:23, 1 Antworten
Daten: dlfkjgkldgjldfgl55.55klsdjfl jfjfjfj8.22fdgddfg {"id":"1","name":"Phase L1","unit":"A",...
IP Cams aufzeichnen?
Bibliothek der Technischen Hochschule Mittelhessen / Giessen, 07.04.2017 09:25, 7 Antworten
Hallo, da nun des öfteren bei uns in der Nachbarschaft eingebrochen wird, würde ich gern mein...
WLAN lässt sich nicht einrichten
Werner Hahn, 21.03.2017 14:16, 2 Antworten
Dell Latitude E6510, Ubuntu 16.4, Kabelbox von Telecolumbus. Nach Anklicken des Doppelpfeiles (o...
"Mit Gwenview importieren" funktioniert seit openSuse 42.2 nicht mehr
Wimpy *, 20.03.2017 13:34, 2 Antworten
Bisher konnte ich von Digitalkamera oder SD-Karte oder USB-Stick Fotos mit Gwenview importieren....