第一件事,在這種情況下做的,就是努力想着怎麼樣,通常,你可以實現這樣的事情。可能是複雜性最大的問題是使用::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.
在那裏,你有它。
你有嘗試過什麼嗎? – KeithC
我做了一段時間後,但我不記得我的代碼是什麼,它已經很久了。我甚至不認爲它有效。我更多是一批人。我並沒有要求任何人爲我寫完整個東西,我只需要一個主要使用':: prev ::'向後移動一個,防止意外腳本中斷並使用多個菜單式提示的kickstart。 – leetbacoon