Skip to content

Instantly share code, notes, and snippets.

@vain
Created February 12, 2011 20:30
Show Gist options
  • Save vain/824089 to your computer and use it in GitHub Desktop.
Save vain/824089 to your computer and use it in GitHub Desktop.
Bash-Menü-Beispiel für Nobuddy
#!/bin/bash
# Mit den Cursor-Tasten bedienbares Menü.
# Hilfsfunktion: Ist ein Item im Array enthalten?
# Siehe auch:
# http://forum.ubuntuusers.de/topic/contains-mit-bash-implementieren/
contains()
{
(( $# > 1 )) && ( [[ "$1" == "$2" ]] || contains "$1" "${@:3}" )
}
# Hilfsfunktion: Entferne ein Item aus dem Array.
remove()
{
key=$1
shift
# Iteriere über alle Einträge in "$@" und gib nur die aus, die nicht
# mit dem Key übereinstimmen. Diese Variante funktioniert folglich
# nicht, falls die Einträge in "$@" Zeilenumbrüche enthalten (da
# dies durch die Ausgabe mit "echo" nicht mehr erkennbar ist). Für
# diesen Zweck hier reicht es jedoch.
for arg
do
[[ "$arg" != "$key" ]] && echo "$arg"
done
}
# Hilfsfunktion, die nur das Menü einmal zeichnet.
draw_menu()
{
# Aktuell fokussierter Punkt.
cur=$1
shift
# Anzahl der selektierten Punkte.
numsel=$1
shift
# Kopiere nun $numsel viele Punkte in das Array $sel. Danach
# befinden sich in "$@" nur noch die auswählbaren Menüpunkte.
sel=()
for (( i = 0; i < numsel; i++ ))
do
sel+=($1)
shift
done
# Gib das Menü aus.
i=0
for arg
do
# Ist dies der fokussierte Eintrag? Zeige dies mit einem
# "Cursor" an.
(( i == cur )) && echo -n '>' || echo -n ' '
# Ist dieser Eintrag augewählt? Markiere ihn mit einem Stern.
contains $i "${sel[@]}" && echo -n '* ' || echo -n ' '
# Menüpunkt selbst.
echo "$arg"
((i++))
done
}
# Hilfsfunktion: Cursorbewegung.
cursor_relative()
{
# Schreibe "cuu1" $2-mal auf's Terminal. Siehe "man terminfo" zwecks
# der "capnames", die man tput mitgeben kann.
for (( i = 0; i < $2; i++ ))
do
( [[ "$1" == up ]] && tput cuu1 || tput cud1 ) >/dev/tty
done
}
# Bash-Menu.
bmenu()
{
# Sichtbarkeit des Cursors ausschalten.
tput civis >/dev/tty
# Bei "Beendigung" den Cursor wieder sichtbar machen. Da diese
# Funktion in einer Subshell ausgeführt werden soll, bedeutet
# "Beendigung" das Ende dieser Subshell. Folglich wird der Cursor
# effektiv dann wieder sichtbar, wenn diese Funktion hier endet (sei
# es auf regulärem Wege oder durch Abbruch/Fehler). Im Zweifel wird
# der Cursor erst ganz am Ende des Skriptes wieder sichtbar. Deshalb
# nach Möglichkeit auf solche Spielereien verzichten.
trap 'tput cnorm >/dev/tty' 0
# Am Anfang: Cursor auf erstem Punkt.
cur=0
# Am Anfang nichts ausgewählt. Die ausgewählten Items werden in
# diesem Array gespeichert.
sel=()
# Zeichne das initiale Menü direkt auf's Terminal.
draw_menu $cur "${#sel[@]}" "${sel[@]}" "$@" >/dev/tty
# Cursor zurücksetzen.
cursor_relative up $#
# Lies in dieser Schleife immer nur ein Zeichen von stdin.
while read -sN1 char
do
# Wenn das erste Zeichen, das du hier gesehen hast, das
# Escape-Zeichen war, dann lies noch zwei weitere Zeichen ein.
# So können die Cursor-Tasten abgefragt werden.
if [[ "$char" == $'\e' ]]
then
read -sN2; char="$char$REPLY"
fi
case "$char" in
j|$'\e[B')
# Auswahl des nächsten Punktes.
((cur++))
;;
k|$'\e[A')
# Auswahl des vorigen Punktes.
((cur--))
;;
q|'')
# Ausgabe der Auswahl, Cursor ans *Ende* des Menüs und
# Schluss.
echo "${sel[@]}"
cursor_relative down $#
return
;;
s)
# Füge aktuelles Item der Auswahl hinzu falls noch
# nicht enthalten.
if ! contains $cur "${sel[@]}"
then
sel+=($cur)
fi
;;
d)
# Entferne aktuellen Punkt aus der Auswahl.
sel=($(remove $cur "${sel[@]}"))
;;
esac
# $cur auf gültigen Bereich beschränken.
(( cur >= $# )) && cur=$(($# - 1))
(( cur < 0 )) && cur=0
# Aktualisiere die Menüansicht und setze den Cursor wieder
# zurück.
draw_menu $cur "${#sel[@]}" "${sel[@]}" "$@" >/dev/tty
cursor_relative up $#
done
}
# Definition der verfügbaren Optionen in einem Array:
options=(
"Option 1"
"noch eine"
"An IPv4 address space walks into a bar ..."
"... and says: ..."
"... a strong CIDR please, I am exhausted."
)
# Testaufruf (Ergebnis wieder ein Array):
sel=($(bmenu "${options[@]}"))
# Paar Infos zur Auswahl und Ausgabe der markierten Elemente:
echo "Du hast ${#sel[@]} Einträge ausgewählt: ${sel[@]}"
for (( i = 0; i < "${#options[@]}"; i++ ))
do
contains $i "${sel[@]}" && echo -e "\t> ${options[$i]}"
done
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment