Shell

方向鍵/進入菜單

  • November 29, 2021

如何在 shell 腳本中創建一個菜單,該菜單將顯示 3 個選項,使用者將使用箭頭鍵移動突出顯示游標並按 Enter 鍵選擇一個?

對話框是您想要實現的目標的絕佳工具。這是一個簡單的 3 選擇菜單範例:

dialog --menu "Choose one:" 10 30 3 \
   1 Red \
   2 Green \
   3 Blue

語法如下:

dialog --menu <text> <height> <width> <menu-height> [<tag><item>]

選擇將被發送到stderr。這是一個使用 3 種顏色的範例腳本。

#!/bin/bash
TMPFILE=$(mktemp)

dialog --menu "Choose one:" 10 30 3 \
   1 Red \
   2 Green \
   3 Blue 2>$TMPFILE

RESULT=$(cat $TMPFILE)

case $RESULT in
   1) echo "Red";;
   2) echo "Green";;
   3) echo "Blue";;
   *) echo "Unknown color";;
esac

rm $TMPFILE

在 Debian 上,您可以dialog通過同名軟體包進行安裝。

bash這是函式形式的純腳本解決方案select_option,僅依賴於ANSI 轉義序列和內置的read.

適用於 OSX 上的 Bash 4.2.45。據我所知,在所有環境中可能無法正常工作的時髦部分是get_cursor_row(), key_input()(用於檢測向上/向下鍵)和cursor_to()功能。

#!/usr/bin/env bash

# Renders a text based list of options that can be selected by the
# user using up, down and enter keys and returns the chosen option.
#
#   Arguments   : list of options, maximum of 256
#                 "opt1" "opt2" ...
#   Return value: selected index (0 for opt1, 1 for opt2 ...)
function select_option {

   # little helpers for terminal print control and key input
   ESC=$( printf "\033")
   cursor_blink_on()  { printf "$ESC[?25h"; }
   cursor_blink_off() { printf "$ESC[?25l"; }
   cursor_to()        { printf "$ESC[$1;${2:-1}H"; }
   print_option()     { printf "   $1 "; }
   print_selected()   { printf "  $ESC[7m $1 $ESC[27m"; }
   get_cursor_row()   { IFS=';' read -sdR -p $'\E[6n' ROW COL; echo ${ROW#*[}; }
   key_input()        { read -s -n3 key 2>/dev/null >&2
                        if [[ $key = $ESC[A ]]; then echo up;    fi
                        if [[ $key = $ESC[B ]]; then echo down;  fi
                        if [[ $key = ""     ]]; then echo enter; fi; }

   # initially print empty new lines (scroll down if at bottom of screen)
   for opt; do printf "\n"; done

   # determine current screen position for overwriting the options
   local lastrow=`get_cursor_row`
   local startrow=$(($lastrow - $#))

   # ensure cursor and input echoing back on upon a ctrl+c during read -s
   trap "cursor_blink_on; stty echo; printf '\n'; exit" 2
   cursor_blink_off

   local selected=0
   while true; do
       # print options by overwriting the last lines
       local idx=0
       for opt; do
           cursor_to $(($startrow + $idx))
           if [ $idx -eq $selected ]; then
               print_selected "$opt"
           else
               print_option "$opt"
           fi
           ((idx++))
       done

       # user key control
       case `key_input` in
           enter) break;;
           up)    ((selected--));
                  if [ $selected -lt 0 ]; then selected=$(($# - 1)); fi;;
           down)  ((selected++));
                  if [ $selected -ge $# ]; then selected=0; fi;;
       esac
   done

   # cursor position back to normal
   cursor_to $lastrow
   printf "\n"
   cursor_blink_on

   return $selected
}

這是一個範例用法:

echo "Select one option using up/down keys and enter to confirm:"
echo

options=("one" "two" "three")

select_option "${options[@]}"
choice=$?

echo "Choosen index = $choice"
echo "        value = ${options[$choice]}"

輸出如下所示,目前選擇的選項使用逆 ansi 著色突出顯示(在降價中很難傳達)。如果需要,可以在print_selected()函式中進行調整。

Select one option using up/down keys and enter to confirm:

 [one] 
  two 
  three 

**更新:**這是一個select_opt包裝上述select_option函式的小擴展,使其易於在case語句中使用:

function select_opt {
   select_option "$@" 1>&2
   local result=$?
   echo $result
   return $result
}

使用 3 個文字選項的範例用法:

case `select_opt "Yes" "No" "Cancel"` in
   0) echo "selected Yes";;
   1) echo "selected No";;
   2) echo "selected Cancel";;
esac

如果有一些已知條目(在這種情況下是 Yes 和 No),您也可以混合使用,並利用$?萬用字元情況的退出程式碼:

options=("Yes" "No" "${array[@]}") # join arrays to add some variable array
case `select_opt "${options[@]}"` in
   0) echo "selected Yes";;
   1) echo "selected No";;
   *) echo "selected ${options[$?]}";;
esac

引用自:https://unix.stackexchange.com/questions/146570