2017-07-26 31 views
2

我想知道如何製作一個bash腳本,裏面有多個菜單。我如何鏈接多個用戶提示以及回到提示的能力?

例如,這裏就是用戶將在運行它,請參閱:

Type the number of choosing: 
1-play 
2-load 
3-exit 

What is your name: 

::上一頁::

Type the number of choosing: 
1-play 
2-load 
3-exit 

What is your name: 

布拉德

Where are you from, Brad? 

賓夕法尼亞

What is your favourite colour? 
1-blue 
2-red 
3-green 
4-grey 
5-magenta 

,sdfhljalk:J-;

What is your favourite colour? 
1-blue 
2-red 
3-green 
4-grey 
5-magenta 

What is your favourite toy? 

列車

What would you like on your sandwich? 

::分組::

What is your favourite toy? 

〜`!@#$%^ & * ()_ + = - {} [] | \'':;?/>。 <,

What is your favourite toy? 

::退出::

Exiting.... 

我爲它做多道歉,我只是想以支付新遊戲的所有基地,我將要作出。我希望這是解決所有問題的問題。

我希望能夠在任何地方輸入::prev::並讓它回到上一個問題,並且我希望::exit::可以在任何地方退出腳本。另外,我希望在帶有編號響應的問題期間無法識別的輸入只是在不繼續的情況下重新加載問題,而對於包含可能導致腳本中斷的字符(例如:;[email protected]# ...)的輸入來重新加載問題而不是中斷。

任何幫助,非常感謝!

順便說一句,我使用的是OS X約塞米蒂

+0

你有嘗試過什麼嗎? – KeithC

+0

我做了一段時間後,但我不記得我的代碼是什麼,它已經很久了。我甚至不認爲它有效。我更多是一批人。我並沒有要求任何人爲我寫完整個東西,我只需要一個主要使用':: prev ::'向後移動一個,防止意外腳本中斷並使用多個菜單式提示的kickstart。 – leetbacoon

回答

2

第一件事,在這種情況下做的,就是努力想着怎麼樣,通常,你可以實現這樣的事情。可能是複雜性最大的問題是使用::prev::來回答一個問題。這意味着我們需要以某種方式表示應用程序狀態,以便我們可以前進或後退。

幸運的是,這很簡單:它基本上只是我們需要的一個堆棧的實現。一些視覺效果:

                ... 
      <--pop--    <--pop-- Location prompt <--pop-- ... 
        Name prompt   Name prompt    ... 
Main menu --push-> Main menu --push-> Main menu  --push-> ... 

這也意味着程序的每個單獨部分都需要是獨立的。我們可以使用函數在shell腳本中輕鬆完成此操作。

因此,我們需要幾件:

  • 功能,顯示菜單,並允許用戶選擇一個值。
  • 顯示文本提示並允許用戶選擇值的功能。
  • 管理表示程序狀態的堆棧的函數。
  • 每個程序的各個功能。

讓我們先寫我們的菜單提示功能。這部分非常簡單。 Bash將使用select循環完成大部分工作,它爲我們打印一個菜單。我們將它包裝起來,以便我們可以處理自定義邏輯,如期待::exit::::prev::以及一些漂亮的打印。

function show_menu { 
    echo "$1" # Print the prompt 
    PS3='> ' # Set prompt string 3 to '> ' 
    select selection in "${menu_items[@]}" # Print menu using the menu_items array 
    do 
    if [[ "$REPLY" =~ ^(::exit::|::prev::)$ ]]; then 
     # If the user types ::exit:: or ::prev::, exit the select loop 
     # and return 1 from the function, with $selection containing 
     # the command the user entered. 
     selection="$REPLY" 
     return 1 
    fi 
    # $selection will be blank if the user did not choose a valid menu item. 
    if [ -z "$selection" ]; then 
     # Display error message if $selection is blank 
     echo 'Invalid input. Please choose an option from the menu.' 
    else 
     # Otherwise, return a success return code. 
     return 0 
    fi 
    done 
} 

現在,我們可以使用此功能,像這樣:

menu_items=('Item 1' 'Item 2' 'Item 3') 
if ! show_menu 'Please choose an item from the menu below.'; then 
    echo "You entered the command $selection." 
fi 

echo "You chose $selection." 

太好了!現在進入議程上的下一個項目,編寫接受來自用戶的文本輸入的代碼。

# Prompt for a required text value. 
function prompt_req { 
    # do...while 
    while : ; do 
    # Print the prompt on one line, then '> ' on the next. 
    echo "$1" 
    printf '> ' 
    read -r selection # Read user input into $selection 
    if [ -z "$selection" ]; then 
     # Show error if $selection is empty. 
     echo 'A value is required.' 
     continue 
    elif [[ "$selection" =~ ^(::exit::|::prev::)$ ]]; then 
     # Return non-success return code if ::exit:: or ::prev:: were entered. 
     return 1 
    elif [[ "$selection" =~ [^a-zA-Z0-9\'\ ] ]]; then 
     # Make sure input only contains a whitelist of allowed characters. 
     # If it has other characters, print an error and retry. 
     echo "Invalid characters in input. Allowed characters are a-z, A-Z, 0-9, ', and spaces." 
     continue 
    fi 
    # This break statement only runs if no issues were found with the input. 
    # Exits the while loop and the function returns a success return code. 
    break 
    done 
} 

很好。此功能類似於第一:

if ! prompt_req 'Please enter a value.'; then 
    echo "You entered the command $selection." 
fi 

echo "You entered '$selection'." 

現在我們有了用戶輸入的處理,我們需要處理與我們的堆棧管理功能的程序流程。這在使用數組的bash中很容易實現。

當程序的一部分運行並完成時,它會要求流程管理器運行下一個功能。流管理器會將下一個函數的名稱推入堆棧,或者將其添加到數組的末尾,然後運行它。如果輸入::prev::,它將彈出堆棧中最後一個函數的名稱,或刪除數組的最後一個元素,然後在其之前運行該函數。

少說話,更多的代碼:

# Program flow manager 
function run_funcs { 
    # Define our "stack" with its initial value being the function name 
    # passed directly to run_funcs 
    funcs=("$1") 
    # do...while 
    while : ; do 
    # Reset next_func 
    next_func='' 
    # Call the last function name in funcs. 
    if "${funcs[${#funcs[@]}-1]}"; then 
     # If the function returned 0, then no :: command was run by the user. 
     if [ -z "$next_func" ]; then 
     # If the function didn't set the next function to run, exit the program. 
     exit 0 
     else 
     # Otherwise, add the next function to run to the funcs array. (push) 
     funcs+=("$next_func") 
     fi 
    else 
     # If the function returned a value other than 0, a command was run. 
     # The exact command run will be in $selection 
     if [ "$selection" == "::prev::" ]; then 
     if [ ${#funcs[@]} -lt 2 ]; then 
      # If there is only 1 function in funcs, then we can't go back 
      # because there's no other function to call. 
      echo 'There is no previous screen to return to.' 
     else 
      # Remove the last function from funcs. (pop) 
      unset funcs[${#funcs[@]}-1] 
     fi 
     else 
     # If $selection isn't ::prev::, then it's ::exit:: 
     exit 0 
     fi 
    fi 
    # Add a line break between function calls to keep the output clean. 
    echo 
    done 
} 

我們run_funcs功能預計:

  • 與第一函數運行的名字被調用,
  • 每個函數運行如果必須繼續執行程序,則會輸出要運行的下一個函數的名稱爲next_func

好的。這應該是非常簡單的工作。現在讓我們來編寫程序:

function main_menu { 
    menu_items=('Play' 'Load' 'Exit') 
    if ! show_menu 'Please choose from the menu below.'; then 
    return 1 
    fi 

    if [ "$selection" == 'Exit' ]; then 
    exit 0 
    fi 

    if [ "$selection" == 'Load' ]; then 
    # Logic to load game state 
    echo 'Game loaded.' 
    fi 

    next_func='prompt_name' 
} 

function prompt_name { 
    if ! prompt_req 'What is your name?'; then 
    return 1 
    fi 
    name="$selection" 

    next_func='prompt_location' 
} 

function prompt_location { 
    if ! prompt_req "Where are you from, $name?"; then 
    return 1 
    fi 
    location="$selection" 

    next_func='prompt_colour' 
} 

function prompt_colour { 
    menu_items=('Blue' 'Red' 'Green' 'Grey' 'Magenta') 
    if ! show_menu 'What is your favourite colour?'; then 
    return 1 
    fi 
    colour="$selection" 

    next_func='prompt_toy' 
} 

function prompt_toy { 
    if ! prompt_req 'What is your favourite toy?'; then 
    return 1 
    fi 
    toy="$selection" 

    next_func='print_player_info' 
} 

function print_player_info { 
    echo "Your name is $name." 
    echo "You are from $location." 
    echo "Your favourite colour is $colour." 
    echo "Your favourite toy is $toy." 

    # next_func is not set, so the program will exit after running this function. 
} 

echo 'My Bash Game' 
echo 
# Start the program, with main_menu as an entry point. 
run_funcs main_menu 

一切都按順序。讓我們試試我們的程序!

$ ./bash_game.sh 
My Bash Game 

Please choose from the menu below. 
1) Play 
2) Load 
3) Exit 
> ::prev:: 
There is no previous screen to return to. 

Please choose from the menu below. 
1) Play 
2) Load 
3) Exit 
> 2 
Game loaded. 

What is your name? 
> Stephanie 

Where are you from, Stephanie? 
> ::prev:: 

What is your name? 
> Samantha 

Where are you from, Samantha? 
> Dubl*n 
Invalid characters in input. Allowed characters are a-z, A-Z, 0-9, ', and spaces. 
Where are you from, Samantha? 
> Dublin 

What is your favourite colour? 
1) Blue 
2) Red 
3) Green 
4) Grey 
5) Magenta 
> Aquamarine 
Invalid input. Please choose an option from the menu. 
> 8 
Invalid input. Please choose an option from the menu. 
> 1 

What is your favourite toy? 
> Teddie bear 

Your name is Samantha. 
You are from Dublin. 
Your favourite colour is Blue. 
Your favourite toy is Teddie bear. 

在那裏,你有它。

+0

您是先生,是我在堆棧溢出中見過的最偉大,最善良,最慷慨的人!我無法感謝你的幫助! – leetbacoon

+2

很高興能幫到你!請注意,這是女士,不是先生。 :) –

+0

我只是有一個問題,然後我應該設置。有什麼辦法可以讓我不必區分大小寫的':: prev ::'和':: exit ::',而不必手工輸出它的每一種形式?例如,我可以鍵入':: eXit ::'或':: PRev ::',它仍然可以正常工作。 – leetbacoon

相關問題