Tombol panah / Masuk menu

11

Bagaimana cara membuat menu dalam skrip shell yang akan menampilkan 3 opsi bahwa pengguna akan menggunakan tombol panah untuk memindahkan kursor sorot dan tekan enter untuk memilih satu?

Mrplow911
sumber
Saya pikir Anda kurang beruntung WRT untuk fungsi panah utama dan menyoroti dalam skrip shell murni (Anda mungkin dapat melakukan yang terakhir dengan tput, tapi saya pikir yang pertama tidak mungkin), tetapi Anda dapat membuat menu sederhana di bash dengan select: tldp.org/LDP/Bash-Beginners-Guide/html/sect_09_06.html
goldilocks
Apakah maksud Anda menu GUI (menggunakan sesuatu seperti [zenity] (Ben Browder) atau yang berbasis teks menggunakan sesuatu seperti ncurses ?
terdon
Saya mencoba membuat menu seperti yang Anda dapatkan jika Anda harus memilih opsi boot untuk windows ("safe mode" "normal" dll)
Mrplow911
1
Ada dialogpaket yang membuat antarmuka terminal faux-GUI dasar dalam skrip.
HalosGhost
@HalosGhost Apakah Anda tahu ada contoh ini?
Mrplow911

Jawaban:

9

dialog adalah alat yang hebat untuk apa yang ingin Anda capai. Berikut adalah contoh menu 3 pilihan sederhana:

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

Sintaksnya adalah sebagai berikut:

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

Pilihan akan dikirim ke stderr. Berikut ini skrip sampel menggunakan 3 warna.

#!/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

Di Debian, Anda dapat menginstal dialogmelalui paket dengan nama yang sama .

John WH Smith
sumber
22

Berikut ini adalah bashsolusi skrip murni dalam bentuk select_optionfungsi, hanya mengandalkan urutan pelarian ANSI dan built-in read.

Bekerja pada Bash 4.2.45 di OSX. Bagian funky yang mungkin tidak berfungsi sama baiknya di semua lingkungan dari yang saya tahu adalah get_cursor_row(), key_input()(untuk mendeteksi tombol naik / turun) dan cursor_to()fungsinya.

#!/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
}

Berikut adalah contoh penggunaannya:

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]}"

Output terlihat seperti di bawah ini, dengan opsi yang dipilih saat ini disorot menggunakan pewarnaan ansi terbalik (sulit untuk disampaikan di sini dalam penurunan harga). Ini dapat diadaptasi dalamprint_selected() fungsi jika diinginkan.

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

  [one] 
   two 
   three 

Pembaruan: Ini sedikit ekstensiselect_opt membungkus select_optionfungsi di atas untuk membuatnya mudah digunakan dalam casepernyataan:

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

Contoh penggunaan dengan 3 opsi literal:

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

Anda juga dapat mencampur jika ada beberapa entri yang diketahui (Ya dan Tidak dalam kasus ini), dan memanfaatkan kode keluar $?untuk kasus wildcard:

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
Alexander Klimetschek
sumber
1
Ini indah dan luar biasa ; terima kasih banyak sudah berbagi! Apakah ini milik Anda sendiri? Apakah ada repo online untuk dikloning? Satu-satunya hal yang saya dapat temukan yang sepertinya ada dalam kontrol versi adalah pada GitHub di stephenmm 's Gist (dengan pengeditan baris ditambahkan) yang menunjuk ke sini, lol. Bekerja pada modifikasi saya sendiri (dalam Intisari, tetapi berencana untuk membuat repo) di sini meskipun saya perlu memperbarui dengan perubahan terbaru masih.
l3l_aze
1
Saya menggunakannya dalam beberapa kode non publik. Menyatukannya dari berbagai potongan yang ditemukan di web :-)
Alexander Klimetschek
Wow; kerja bagus. Saya memulai repo dengan modifikasi saya di https://github.com/l3laze/sind . Sejauh ini perbedaan terbesar adalah penanganan input yang ditingkatkan dan penambahan judul bar. Saya berharap untuk menambahkan pengeditan tunggal dan multi-line, tetapi belum melakukan apa-apa terhadap mereka yang belum melihat beberapa kode
l3l_aze