Can anyone refer me to examples of interactive selection menus written in
Here's the function:
sh
? I realize it would be easier to use {Dialog, Perl, Python, Bash, C}, but I'm wondering what's possible using only sh
and the standard utilities. So far, I have a function that cycles through a set of options using tab and arrows, with enter to select the current item:
sh:
$ ./menu.sh
Select option from heredoc (1/3): vanilla
You selected index 0 (vanilla)
Select option from string (2/3): chocolate
You selected index 1 (chocolate)
$
Here's the function:
sh:
#!/bin/sh
select_one() {
# select a line from $1 with optional prompt $2
lines=$(echo "$1" | awk '{gsub(/\\n/,"\n"); print}')
selected=0
# count lines to display in $1 and exit early
linect=$(($(echo "$lines" | wc -l)))
if [ "$linect" -eq 0 ]; then
echo "0"
return 1
elif [ "$linect" -eq 1 ]; then
echo "0"
return 0
fi
# save tty settings
tty_settings=$(stty -g)
stty -icanon min 1 time 0 -echo cbreak
# clear the line by printing spaces
cols=$(($(tput cols) - 1))
el=$(printf "%*s" "$cols" "")
while true; do
# clear the line and display current selection
idx=$((selected + 1))
line=$(echo "$lines" | sed -n "${idx}p")
printf "%s%s%s%s(%s/%s): %s" "$(tput cr)" "$el" "$(tput cr)" "$2" "$idx" "$linect" "$line" > /dev/tty
# read one byte of input
byte=$(dd if="$(tty)" bs=1 count=1 2>/dev/null)
char=$(printf "%d" "'$byte'")
# look for tabs, the last byte of arrows, and enter
if [ "$char" = 9 ] || [ "$char" = 66 ] || [ "$char" = 67 ]; then
selected=$((selected + 1))
elif [ "$char" = 90 ] || [ "$char" = 65 ] || [ "$char" = 68 ]; then
selected=$((selected - 1))
elif [ "$char" = 39 ]; then
break
fi
# wrap selected index
selected=$(((selected + linect) % linect))
done
# restore tty settings
stty "$tty_settings"
echo "$selected"
return 0
}
lines=$(cat <<'EOF'
vanilla
chocolate
strawberry
EOF
)
idx=$(select_one "$lines" "Select option from heredoc ")
line=$(echo "$lines" | awk '{gsub(/\\n/,"\n"); print}' | sed -n "$((idx + 1))p")
echo
echo "You selected index $idx ($line)"
echo
lines="vanilla\nchocolate\nstrawberry"
idx=$(select_one "$lines" "Select option from string ")
line=$(echo "$lines" | awk '{gsub(/\\n/,"\n"); print}' | sed -n "$((idx + 1))p")
echo
echo "You selected index $idx ($line)"