मैं शेल स्क्रिप्ट में एक चयन मेनू कैसे बना सकता हूं?


134

मैं एक सरल बैश स्क्रिप्ट बना रहा हूं और मैं इस तरह से एक चयन मेनू बनाना चाहता हूं:

$./script

echo "Choose your option:"

1) Option 1  
2) Option 2  
3) Option 3  
4) Quit  

और उपयोगकर्ता की पसंद के अनुसार, मैं चाहता हूं कि विभिन्न कार्यों को निष्पादित किया जाए। मैं एक बैश शेल स्क्रिप्टिंग नॉब हूं, मैंने कुछ उत्तरों के लिए वेब खोजा है, लेकिन वास्तव में कुछ भी ठोस नहीं मिला है।


1
प्रश्न पुराना और संरक्षित है, लेकिन मैं fzf का उपयोग करता हूं। कोशिश करो seq 10 | fzf। दोष यह है कि डिफ़ॉल्ट रूप से fzf स्थापित नहीं है। आप यहाँ fzf पा सकते हैं: github.com/junegunn/fzf
लिंच

जवाबों:


152
#!/bin/bash
# Bash Menu Script Example

PS3='Please enter your choice: '
options=("Option 1" "Option 2" "Option 3" "Quit")
select opt in "${options[@]}"
do
    case $opt in
        "Option 1")
            echo "you chose choice 1"
            ;;
        "Option 2")
            echo "you chose choice 2"
            ;;
        "Option 3")
            echo "you chose choice $REPLY which is $opt"
            ;;
        "Quit")
            break
            ;;
        *) echo "invalid option $REPLY";;
    esac
done

breakजहाँ भी आपको selectबाहर निकलने के लिए लूप की आवश्यकता हो, बयान जोड़ें । यदि breakकोई प्रदर्शन नहीं किया गया है, तो selectबयान लूप और मेनू फिर से प्रदर्शित होता है।

तीसरे विकल्प में, मैंने उन चर को शामिल किया, जो selectकथन द्वारा निर्धारित किए गए हैं कि आप उन मूल्यों तक पहुंच प्राप्त कर सकते हैं। यदि आप इसे चुनते हैं, तो यह आउटपुट होगा:

you chose choice 3 which is Option 3

आप देख सकते हैं कि $REPLYप्रांप्ट पर आपके द्वारा दर्ज स्ट्रिंग है। यह सरणी में एक सूचकांक के रूप में उपयोग किया जाता है जैसे ${options[@]}कि सरणी 1 आधारित थे। चर $optमें उस सूचकांक से स्ट्रिंग शामिल है।

ध्यान दें कि selectइस तरह सीधे बयान में विकल्प एक साधारण सूची हो सकती है :

select opt in foo bar baz 'multi word choice'

लेकिन आप विकल्पों में से एक में रिक्त स्थान होने के कारण ऐसी सूची को स्केलर चर में नहीं डाल सकते।

यदि आप फ़ाइलों के बीच चयन कर रहे हैं, तो आप फ़ाइल ग्लोबिंग का भी उपयोग कर सकते हैं:

select file in *.tar.gz

@ अब्दुल: यह इच्छित व्यवहार है। "छोड़ो" विकल्प एक निष्पादित करता है breakजो selectलूप से बाहर निकलता है । आप breakकहीं भी अपनी आवश्यकता के अनुसार जोड़ सकते हैं बैश मैनुअल राज्यों "आदेश प्रत्येक चयन जब तक एक को तोड़ने आदेश निष्पादित किया जाता है, जिस पर चयन आदेश कम्प्लिट्स बात के बाद क्रियान्वित कर रहे हैं।"
डेनिस विलियमसन

FWIW, GNU बैश 4.3.11 के साथ 14.04 पर इसे चलाने पर लाइन 9: ./test.sh: line 9: syntax error near unexpected token "विकल्प 1" / .estest.sh: पंक्ति 9: `" विकल्प 1 ") '`
ब्रायन मॉर्टन

@ ब्रायनमॉर्टन: मैं ऐसा नहीं कर सकता। यह संभावना है कि आप स्क्रिप्ट में एक उद्धरण या कुछ अन्य चरित्र पहले याद कर रहे हैं (या एक अतिरिक्त है)।
डेनिस विलियमसन

2
@dtmland: कमांड के PS3लिए संकेत है select। इसका उपयोग स्वचालित रूप से किया जाता है और इसे स्पष्ट रूप से संदर्भित करने की आवश्यकता नहीं होती है। PS3और selectप्रलेखन।
डेनिस विलियमसन

1
@ क्रिसियन: नहीं, यह नहीं होना चाहिए (लेकिन अगर मैं मूल्यों के बजाय बयान में सूचकांकों का उपयोग कर सकता हूं )। मुझे लगता है कि मूल्यों का उपयोग बेहतर दस्तावेजों को बयान के वर्गों की कार्यक्षमता के लिए करता है। $optionscasecase
डेनिस विलियमसन

56

प्रत्येक उत्तर के लिए एक नया उत्तर नहीं है , लेकिन चूंकि अभी तक कोई स्वीकृत उत्तर नहीं है, इसलिए यहां कुछ कोडिंग युक्तियां और ट्रिक्स हैं, दोनों के लिए चयन और ज़ेन:

title="Select example"
prompt="Pick an option:"
options=("A" "B" "C")

echo "$title"
PS3="$prompt "
select opt in "${options[@]}" "Quit"; do 

    case "$REPLY" in

    1 ) echo "You picked $opt which is option $REPLY";;
    2 ) echo "You picked $opt which is option $REPLY";;
    3 ) echo "You picked $opt which is option $REPLY";;

    $(( ${#options[@]}+1 )) ) echo "Goodbye!"; break;;
    *) echo "Invalid option. Try another one.";continue;;

    esac

done

while opt=$(zenity --title="$title" --text="$prompt" --list \
                   --column="Options" "${options[@]}"); do

    case "$opt" in
    "${options[0]}" ) zenity --info --text="You picked $opt, option 1";;
    "${options[1]}" ) zenity --info --text="You picked $opt, option 2";;
    "${options[2]}" ) zenity --info --text="You picked $opt, option 3";;
    *) zenity --error --text="Invalid option. Try another one.";;
    esac

done

उल्लेखनीय है:

  • दोनों तब तक लूप करेंगे जब तक उपयोगकर्ता स्पष्ट रूप से क्विट (या ज़ेनिटी के लिए रद्द) का चयन नहीं करता। यह इंटरैक्टिव स्क्रिप्ट मेनू के लिए एक अच्छा तरीका है: एक विकल्प चुने जाने और कार्रवाई करने के बाद, मेनू को फिर से किसी अन्य विकल्प के लिए प्रस्तुत किया जाता है। यदि विकल्प का अर्थ केवल एक बार होना है, तो बस के breakबाद का उपयोग करें esac(जिनीटी दृष्टिकोण को और भी कम किया जा सकता है)

  • दोनों caseमूल्य-आधारित होने के बजाय सूचकांक आधारित हैं। मुझे लगता है कि यह कोड करना और बनाए रखना आसान है

  • एरे का उपयोग zenityदृष्टिकोण के लिए भी किया जाता है ।

  • "छोड़ो" विकल्प प्रारंभिक, मूल विकल्पों में से नहीं है। जरूरत पड़ने पर इसे "जोड़ा" जाता है, ताकि आपकी सरणी साफ रहे। Afterall, वैसे भी "क्विट" के लिए ज़ेनिटी की आवश्यकता नहीं है, उपयोगकर्ता बाहर निकलने के लिए बस "रद्द करें" (या विंडो बंद करें) पर क्लिक कर सकता है। ध्यान दें कि दोनों विकल्पों के समान, अछूते सरणी का उपयोग कैसे करते हैं।

  • PS3और REPLYvar का नाम बदला नहीं जा सकता। selectउन का उपयोग करने के लिए हार्डकोड है। स्क्रिप्ट (विकल्प, विकल्प, प्रॉम्प्ट, शीर्षक) में अन्य सभी चर आपके इच्छित कोई भी नाम हो सकते हैं, बशर्ते आप समायोजन करें


बहुत बढ़िया व्याख्या। धन्यवाद। यह प्रश्न अभी भी Google पर उच्च स्थान पर है, इसलिए बहुत बुरा यह बंद है।
माउंटेनएक्स

आप एक ही इस्तेमाल कर सकते हैं caseके लिए संरचना selectसंस्करण है कि आप के लिए उपयोग कर रहे हैं zenity: संस्करण case "$opt" in . . . "${options[0]}" ) . . .(के बजाय $REPLYऔर सूचकांक 1, 2और 3)।
डेनिस विलियमसन

@ डेनिसविलियम्सन, हां मैं कर सकता था, और "वास्तविक" कोड में दोनों मामलों में समान संरचना का उपयोग करना बेहतर होगा। मैं जानबूझकर संबंध $REPLY, सूचकांक और मूल्यों के बीच संबंध दिखाना चाहता था ।
MestreLion

55

का उपयोग कर dialog, कमांड इस तरह दिखेगा:

डायलॉग - क्लियर - बैकटाइटल "बैकिटेल इधर" - टाइटल "टाइटल यहां" --मेनू "निम्नलिखित विकल्पों में से एक चुनें:" 15 40 4 \
1 "विकल्प 1"
2 "विकल्प 2" \
3 "विकल्प 3"

यहां छवि विवरण दर्ज करें

इसे स्क्रिप्ट में रखना:

#!/bin/bash

HEIGHT=15
WIDTH=40
CHOICE_HEIGHT=4
BACKTITLE="Backtitle here"
TITLE="Title here"
MENU="Choose one of the following options:"

OPTIONS=(1 "Option 1"
         2 "Option 2"
         3 "Option 3")

CHOICE=$(dialog --clear \
                --backtitle "$BACKTITLE" \
                --title "$TITLE" \
                --menu "$MENU" \
                $HEIGHT $WIDTH $CHOICE_HEIGHT \
                "${OPTIONS[@]}" \
                2>&1 >/dev/tty)

clear
case $CHOICE in
        1)
            echo "You chose Option 1"
            ;;
        2)
            echo "You chose Option 2"
            ;;
        3)
            echo "You chose Option 3"
            ;;
esac

मैं ध्यान दें कि मैं लाइन डाल करना चाहते हैं TERMINAL=$(tty)मेरी स्क्रिप्ट के शीर्ष पर और उसके बाद में CHOICEचर परिभाषा मैं बदल 2>&1 >/dev/ttyकरने के लिए 2>&1 >$TERMINALकरता है, तो स्क्रिप्ट एक अलग टर्मिनल संदर्भ में चलाया गया था पुनर्निर्देशन मुद्दों से बचने के।
Shrout1

1
--backtitleपैरामीटर क्या करता है?
TRGG

2
यह पृष्ठभूमि में ब्लूस्क्रीन का शीर्षक है। आप इसे स्क्रीनशॉट के ऊपरी बाएँ कोने में देख सकते हैं, जिसमें लिखा है "Backtitle here"।
अलाअ अली

14

आप विकल्प बनाने के लिए इस सरल स्क्रिप्ट का उपयोग कर सकते हैं

#! / Bin / bash
गूंज "ऑपरेशन का चयन करें ************"
इको "1) ऑपरेशन 1"
इको "2) ऑपरेशन 2"
इको "3) ऑपरेशन 3"
इको "4) ऑपरेशन 4" 
एन पढ़ें मामला $ n में 1) गूंज "आपने विकल्प 1 चुना" ;; 2) गूंज "आपने विकल्प 2 चुना" ;; 3) गूंज "आपने विकल्प 3 चुना" ;; 4) गूंज "आपने विकल्प 4 चुना" ;; *) गूंज "अमान्य विकल्प" ;; esac


8

मेरे पास एक और विकल्प है जो इन उत्तरों का एक मिश्रण है लेकिन जो अच्छा है वह यह है कि आपको केवल एक कुंजी दबाने की आवश्यकता है और फिर स्क्रिप्ट -nपढ़ने के विकल्प के लिए धन्यवाद जारी है । इस उदाहरण में, हम शटडाउन, रिबूट करने के लिए संकेत दे रहे हैं, या केवल ANSहमारे चर के रूप में स्क्रिप्ट का उपयोग करके बाहर निकलें और उपयोगकर्ता को केवल ई, आर, या एस को प्रेस करना होगा। मैंने डिफ़ॉल्ट को बाहर निकलने के लिए भी सेट किया है, यदि प्रवेश दबाया जाता है तो स्क्रिप्ट बाहर निकल जाएगा।

read -n 1 -p "Would you like to exit, reboot, or shutdown? (E/r/s) " ans;

case $ans in
    r|R)
        sudo reboot;;
    s|S)
        sudo poweroff;;
    *)
        exit;;
esac

7

चूंकि यह उबंटू पर लक्षित है, इसलिए आपको उपयोग करने के लिए जो भी बैकएंड डिबेंक्फ़ कॉन्फ़िगर किया गया है, उसका उपयोग करना चाहिए। आप के साथ debconf बैकएंड का पता लगा सकते हैं:

sudo -s "echo get debconf/frontend | debconf-communicate"

यदि यह "संवाद" कहता है, तो इसका उपयोग संभव है whiptailया dialog। ल्यूसिड पर whiptail

यदि वह विफल रहता है, तो डेनिस विलियमसन द्वारा बताए अनुसार "बैश" का उपयोग करें।


3
यह संभवतः उस प्रश्न के लिए ओवरकिल है, लेकिन व्हिपटेल और संवाद का उल्लेख करने के लिए +1! मैं उन आज्ञाओं से वाकिफ नहीं था ... बहुत प्यारी!
MestreLion

7
#! / Bin / श
मेनू दिखाओ(){
    सामान्य = `प्रतिध्वनि" \ 033 [मी "`
    मेनू = `प्रतिध्वनि" \ 033 [36 मी "` # बाल
    संख्या = `प्रतिध्वनि" \ 033 [33 मी "` # साल
    bgred = `echo" \ 033 [41m "`
    fgred = `echo" \ 033 [31 मी "`
    Printf "\ n $ {मेनू} ********************************* *** $ {सामान्य} \ n "
    प्रिंटफ़ "$ {मेनू} ** $ {संख्या} 1) $ {मेनू} माउंट ड्रॉपबॉक्स $ {सामान्य} \ n"
    printf "$ {मेनू} ** $ {संख्या} 2) $ {मेनू} माउंट USB 500 गिग ड्राइव $ {सामान्य} \ n"
    प्रिंटफ "$ {मेनू} ** $ {संख्या} 3) $ {मेनू} अपाचे $ {सामान्य} \ n" को पुनरारंभ करें
    Printf "$ {मेनू} ** $ {संख्या} 4) $ {मेनू} ssh फ्रॉस्ट टॉमकैट सर्वर $ {सामान्य} \"
    प्रिंटफ "$ {मेनू} ** $ {संख्या} 5) $ {मेनू} कुछ अन्य कमांड $ {सामान्य} \ n"
    Printf "$ {मेनू} *********************************** * $ {सामान्य} \ n "
    प्रिंटफ "कृपया मेनू विकल्प चुनें और बाहर निकलने के लिए $ {fgred} x दर्ज करें। $ {सामान्य}"
    पठन विकल्प
}

option_picked () {
    msgcolor = `इको” \ 033 [01; 31 मीटर ”` # बोल्ड रेड
    सामान्य = `प्रतिध्वनि" \ 033 [00; 00 मीटर "` # सामान्य सफेद
    संदेश = $ {@: - "$ {सामान्य} त्रुटि: कोई संदेश पारित नहीं हुआ"}
    प्रिंटफ "$ {msgcolor} $ {संदेश} $ {सामान्य} \ n"
}

स्पष्ट
मेनू दिखाओ
जबकि [$ ऑप्ट! = '']
    करना
    अगर [$ ऑप्ट = '']; फिर
      बाहर जाएं;
    अन्य
      केस $ ऑप्ट इन
        1) स्पष्ट;
            Option_picked "विकल्प 1 उठाया";
            प्रिंटफ "सुडो माउंट / देव / sdh1 / mnt / ड्रॉपबॉक्स /; # 3 टेराबाइट";
            मेनू दिखाओ;
        ;;
        2) स्पष्ट;
            Option_picked "विकल्प 2 पिक किया गया";
            प्रिंटफ "सुडो माउंट / देव / sdi1 / mnt / usbDrive; # 500 गिग ड्राइव";
            मेनू दिखाओ;
        ;;
        3) स्पष्ट;
            Option_picked "विकल्प 3 पिक किया गया";
            printf "sudo service apache2 पुनरारंभ";
            मेनू दिखाओ;
        ;;
        4) स्पष्ट;
            Option_picked "विकल्प 4 पिक किया गया";
            printf "ssh lmesser @ -p 2010";
            मेनू दिखाओ;
        ;;
        एक्स) से बाहर निकलें;
        ;;
        \ N) से बाहर निकलें;
        ;;
        *)स्पष्ट;
            option_picked "मेनू से एक विकल्प चुनें";
            मेनू दिखाओ;
        ;;
      esac
    फाई
किया हुआ

2
मुझे पता है कि यह पुराना है, लेकिन संकलन के लिए #! / बिन / बैश पढ़ने के लिए पहली पंक्ति की आवश्यकता है।
जेक्लर

कोड समीक्षा: स्टेटमेंट में वेरिएबल $से गायब है । बयान अनावश्यक है। असंगत इंडेंटेशन। कुछ जगहों पर उपयोग करना जहां यह 'show_menu show_menu` होना चाहिए , प्रत्येक में दोहराया जाने के बजाय लूप के शीर्ष पर रखा जा सकता है । असंगत इंडेंटेशन। एकल वर्ग कोष्ठक और दोगुनी वाले लोगों का मिश्रण। के बजाय हार्ड कोडित एएनएसआई दृश्यों का उपयोग करना । ऑल-कैप संस्करण नामों का उपयोग अनुशंसित नहीं है। बुलाया जाना चाहिए । के बजाय बैकटिक्स का उपयोग । फ़ंक्शन की परिभाषा सुसंगत होनी चाहिए और इसका उपयोग नहीं करना चाहिए ...$optwhileifmenu. casetputFGREDbgred$()
डेनिस विलियमसन

... दोनों एक साथ बनते हैं। टर्मिनल अर्धविराम की आवश्यकता नहीं है। कुछ रंगों आदि को दो बार परिभाषित किया गया है। के साथ मामले \nको कभी भी निष्पादित नहीं किया जाएगा। शायद ज्यादा।
डेनिस विलियमसन

6

मैंने ज़ेनिटी का उपयोग किया है, जो उबंटू में हमेशा लगता है, बहुत अच्छी तरह से काम करता है और इसमें कई क्षमताएं हैं। यह एक संभावित मेनू का एक स्केच है:

#! /bin/bash

selection=$(zenity --list "Option 1" "Option 2" "Option 3" --column="" --text="Text above column(s)" --title="My menu")

case "$selection" in
"Option 1")zenity --info --text="Do something here for No1";;
"Option 2")zenity --info --text="Do something here for No2";;
"Option 3")zenity --info --text="Do something here for No3";;
esac

ऊप्स! इस स्निपेट की उपस्थिति के बारे में खेद, पहली बार पोस्ट करने और लगता है कि शायद HTML को बंद करना होगा
LazyEchidna

बेहतर, संपादन में 'कोड नमूना' बदल गया, इसके बारे में खेद है
LazyEchidna

5

बैश फैंसी मेनू

इसे पहले आज़माएँ, फिर विस्तृत विवरण के लिए मेरे पेज पर जाएँ ... बाहरी पुस्तकालयों या संवाद या ज़ेनिटी जैसे कार्यक्रमों की कोई ज़रूरत नहीं ...

#/bin/bash
# by oToGamez
# www.pro-toolz.net

      E='echo -e';e='echo -en';trap "R;exit" 2
    ESC=$( $e "\e")
   TPUT(){ $e "\e[${1};${2}H";}
  CLEAR(){ $e "\ec";}
  CIVIS(){ $e "\e[?25l";}
   DRAW(){ $e "\e%@\e(0";}
  WRITE(){ $e "\e(B";}
   MARK(){ $e "\e[7m";}
 UNMARK(){ $e "\e[27m";}
      R(){ CLEAR ;stty sane;$e "\ec\e[37;44m\e[J";};
   HEAD(){ DRAW
           for each in $(seq 1 13);do
           $E "   x                                          x"
           done
           WRITE;MARK;TPUT 1 5
           $E "BASH SELECTION MENU                       ";UNMARK;}
           i=0; CLEAR; CIVIS;NULL=/dev/null
   FOOT(){ MARK;TPUT 13 5
           printf "ENTER - SELECT,NEXT                       ";UNMARK;}
  ARROW(){ read -s -n3 key 2>/dev/null >&2
           if [[ $key = $ESC[A ]];then echo up;fi
           if [[ $key = $ESC[B ]];then echo dn;fi;}
     M0(){ TPUT  4 20; $e "Login info";}
     M1(){ TPUT  5 20; $e "Network";}
     M2(){ TPUT  6 20; $e "Disk";}
     M3(){ TPUT  7 20; $e "Routing";}
     M4(){ TPUT  8 20; $e "Time";}
     M5(){ TPUT  9 20; $e "ABOUT  ";}
     M6(){ TPUT 10 20; $e "EXIT   ";}
      LM=6
   MENU(){ for each in $(seq 0 $LM);do M${each};done;}
    POS(){ if [[ $cur == up ]];then ((i--));fi
           if [[ $cur == dn ]];then ((i++));fi
           if [[ $i -lt 0   ]];then i=$LM;fi
           if [[ $i -gt $LM ]];then i=0;fi;}
REFRESH(){ after=$((i+1)); before=$((i-1))
           if [[ $before -lt 0  ]];then before=$LM;fi
           if [[ $after -gt $LM ]];then after=0;fi
           if [[ $j -lt $i      ]];then UNMARK;M$before;else UNMARK;M$after;fi
           if [[ $after -eq 0 ]] || [ $before -eq $LM ];then
           UNMARK; M$before; M$after;fi;j=$i;UNMARK;M$before;M$after;}
   INIT(){ R;HEAD;FOOT;MENU;}
     SC(){ REFRESH;MARK;$S;$b;cur=`ARROW`;}
     ES(){ MARK;$e "ENTER = main menu ";$b;read;INIT;};INIT
  while [[ "$O" != " " ]]; do case $i in
        0) S=M0;SC;if [[ $cur == "" ]];then R;$e "\n$(w        )\n";ES;fi;;
        1) S=M1;SC;if [[ $cur == "" ]];then R;$e "\n$(ifconfig )\n";ES;fi;;
        2) S=M2;SC;if [[ $cur == "" ]];then R;$e "\n$(df -h    )\n";ES;fi;;
        3) S=M3;SC;if [[ $cur == "" ]];then R;$e "\n$(route -n )\n";ES;fi;;
        4) S=M4;SC;if [[ $cur == "" ]];then R;$e "\n$(date     )\n";ES;fi;;
        5) S=M5;SC;if [[ $cur == "" ]];then R;$e "\n$($e by oTo)\n";ES;fi;;
        6) S=M6;SC;if [[ $cur == "" ]];then R;exit 0;fi;;
 esac;POS;done

1
यह केवल बैश, थोड़े शांत के साथ काम करता है।
लेटन एवेरसन

2
अच्छा! "फिर विस्तृत विवरण के लिए मेरे पेज पर जाएं" क्या कोई लिंक है?
ओक


2

पहले से ही सर्वरफॉल्ट में एक ही प्रश्न का उत्तर दिया गया है। वहाँ समाधान व्हिप्टेल का उपयोग करता है ।


धन्यवाद, लेकिन जैसा कि मेरी स्क्रिप्ट मुख्यधारा की खपत के लिए है, मैं नहीं चाहता कि यह कोई अतिरिक्त निर्भरता हो। लेकिन मैं इसे बुकमार्क करूंगा कि भविष्य में उपयोग के लिए कौन जानता है।
डैनियल रोड्रिग्स

-1

मान लें कि आप एक सादे शेल स्क्रिप्ट मेनू (कोई फैंसी यूआई) का उपयोग करना चाहते हैं, तो http://www.tldp.org/LDP/abs/html/testbranch.html से मेनू उदाहरण की जांच करें


1
एडवांस बैश-स्क्रिप्टिंग गाइड में अन्य स्निपेट्स की तरह, उन स्निपेट्स में बग और बुरे अभ्यास होते हैं।
जिरह
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.