如何使用 Bash 實現基於文字的測驗?

如何使用 Bash 實現基於文字的測驗?

是否有一種在 Bash 中實現測驗的好方法,每次問題都是隨機的,並且記錄用戶的不同反應(正確、錯誤、通過),然後在測驗結束時給出?

我想數組可以用來存儲測驗問題和答案,測驗問題本身可以在一段時間內或直到循環內提出,並且可以遞增各種問題直到到達最後一個問題,然後可以評估用戶的表現通過使用各種條件語句?


請注意這個老問題討論了圖形測驗軟體並明確提到了聲音/圖形等,而我僅討論基於命令列文字的測驗。

(我已經在下面回答了我自己的問題......但是如果有人有另一個 Bash 測驗框架,請發布。)

答案1

我提出的測驗框架發佈在下面,也發佈在GitHub(原始使用者 GitHub 頁面已不存在)。

由於它看起來相當複雜,我最好解釋一下它是如何工作的。

在腳本中,迄今為止所有發行版的 Ubuntu 版本和代號都儲存在兩個陣列中。然後變數被初始化外部直到循環,因此它們可以根據需要在循環中使用 C 樣式(( var++ ))表示法遞增。

然後,我為問題編號建立另一個數組,在本例中,對於 19 個問題,我必須包含 0 - 18(含):

questions_order=(15 4 1 10 8 3 13 0 11 16 2 7 5 17 6 9 14 18 12)

然後使用它來打亂它shuf並創建一個新數組並將其用於該腳本的特定運行:

shuffled_order=( $(shuf -n19 -e ${questions_order[@]}) )

這必須完成外部循環的,因為我只希望它執行一次。

簡而言之,現在循環運行直到當前問題數等於變量"${target_questions}",然後給出結果。

當用戶對問題做出回答時,它將由case聲明中的各種條件進行解釋。無答案(空白)、錯誤答案、正確答案和通過都被檢測到並接收響應,並且各種變量都被遞增(例如,對於錯誤答案,(( wrong++ )))。

當回答最後一個問題後,將評估使用者的表現,其中正確、錯誤答案和通過次數(如果有)會被轉發,並且還會給出所花費的時間。

這是一個簡短的解釋,但腳本本身有各種註釋,應該會使工作原理清晰(ish)!它可以適用於任何其他類型的測驗,例如關於首都和國家的測驗。

將腳本複製到檔案中後,使其可執行 ( chmod u+x),然後運行它./scriptname或將其放入資料夾中~/bin,並像任何其他程式一樣按名稱呼叫它,假設您的檔案~/bin位於PATH.

#!/usr/bin/env bash

# by mik, aka Exactus29, https://github.com/Exactus29
# 
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.

# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.

# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

######### 

# Requires at least bash 3.1. As it makes extensive use of arrays, it would be a pain to 
# try to write it to be sh compatible, but it may be possible.

# arrays for questions and answers
codenames=("Warty Wharthog" "Hoary Hedgehog" "Breezy Badger" "Dapper Drake" "Edgy Eft" "Feisty Fawn" 
"Gutsy Gibbon" "Hardy Heron" "Intrepid Ibex" "Jaunty Jackalope" "Karmic Koala" "Lucid Lynx" "Maverick Meerkat" 
"Natty Narwahl" "Oneric Ocelot" "Precise Pangolin" "Quantal Quentzal" "Raring Ringtail" "Saucy Salamander")
versions=(4.10 5.04 5.10 6.06 6.10 7.04 7.10 8.04 8.10 9.04 9.10 10.04 10.10 
11.04 11.10 12.04 12.10 13.04 13.10)

# must intialize quest_index at -1 so we can use array index 0
quest_index=-1
target_questions=19
# we start question counter at 0, so we can increment it to the value in number_questions
questions=0 
# the other variables below will all be incremented as necessary in the quiz
correct=0 
wrong=0
no_response=0
pass=0

# declare questions_order array and then shuffle it and use that for this run of the script.
# must be declared outside of the loop, as only want it executed once
questions_order=(15 4 1 10 8 3 13 0 11 16 2 7 5 17 6 9 14 18 12)
shuffled_order=( $(shuf -n19 -e ${questions_order[@]}) )

printf "\nPress 0 to exit the quiz at any time.\n"
printf "You can ignore case in your answers, but correct spelling of the Ubuntu codenames is crucial.\n"
printf "Please enter the word pass if you don't know the answer.\n\n\n"


timer_begin=$SECONDS


until (( questions == target_questions )); do
 
(( questions++ ))

(( quest_index++ ))

new_index=$( echo ${shuffled_order[$quest_index]})

# alternate style of questions, separate odd and even 

    if (( questions % 2 > 0 )); then 
        new_question="${codenames[$new_index]}"
        ans="${versions[$new_index]}"
        question_text="Ubuntu release had the development codename"
    else
        new_question="${versions[$new_index]}"
        ans="${codenames[$new_index]}"
        question_text="was the Ubuntu development codename (both adjective and animal) for release"
    fi

read -p "(${questions}) What ${question_text} ${new_question}? " response 

# necessary to switch on nocasematch to cover if the answer is in capitals, as can't use normal [Mm] in case statement
shopt -s nocasematch

    case $response in 
    "$ans") 
          printf "Well done, correct answer. " 
          (( correct++ ))
          if (( questions < target_questions )); then 
              printf "Next question.\n\n" 
          else
              printf "\nHowever, the quiz has now finished. Let us calculate your performance...\n"
              sleep 1
          fi
       ;; 
       0) 
          printf "\nOk, time to finish with the quiz.\n"
          break
       ;;
    "pass")
         (( pass++ ))
         printf "Ok, you passed on this one..."
         if (( pass >= 10 )); then 
            printf "The passes are mounting, as you have now had ${pass} passes.\n"
         elif (( pass >= 2 )); then
            printf "Please try to give an answer, as you have now had ${pass} passes.\n" 
         fi
            
         if (( questions < target_questions )); then 
            printf "Let us try another question.\n\n"
         else
            printf "\nHowever, the quiz has now finished. Let us calculate your performance.....\n"
            sleep 1
         fi
         
       ;;
       *) 
          if [[ -z ${response} ]]; then
              printf "Please provide an answer, as a blank answer is counted as a wrong answer; "
              (( no_response++ ))
              (( wrong++ ))
                if (( no_response == 1  )); then
                    printf "${no_response} blank response has been given so far. \n"
                elif (( no_response > 1 )); then
                    printf "${no_response} blank responses have been given so far. \n"   
                fi
          else
              (( wrong++ ))
                if  (( questions % 2 > 0 )); then
                    printf "That is incorrect..."
                else 
                    printf "That is incorrect, ${response} was not the codename of ${new_question}. \n"
                fi
          fi
           
         if (( questions < target_questions )); then 
            printf "Let us try another question.\n\n"
         else
            printf "\nHowever, the quiz has now finished. Let us calculate your performance.....\n"
            sleep 1
         fi
       ;;
    esac

done

quiz_duration=$(( SECONDS - timer_begin ))
# could further process the $quiz_duration if it is over 60 seconds, and output the total in
# minutes and seconds using bc or awk

# checking against target_questions here, i.e. all of the questions
if (( questions == target_questions )); then 
    if (( correct == target_questions )); then
        printf "\nYou got them all right in ${quiz_duration} seconds, well done!\n\n"
    elif (( no_response == target_questions )); then 
        printf "\nYou gave ${no_response} blank responses, and so effectively gave ${no_response} wrong answers.\n\n"                
    elif (( wrong == target_questions )); then
        printf "\nYou got them all wrong in ${quiz_duration} seconds, oh dear!\n\n"
    elif (( pass == target_questions )); then 
        printf "\nYou passed on all the questions. Was this just a trial run?\n\n"             
    else
        printf "\nOut of ${target_questions} questions, you got "
        # have to do this now because we have added the pass option, as you can have none wrong/correct,
        # some correct/wrong, and some passes
        (( wrong > 0 )) && printf "${wrong} wrong and "
        (( correct > 0 )) && printf "${correct} correct "
        
        if (( pass == 0 )); then
            printf "with no passess at all in a time of ${quiz_duration} seconds.\n "
        elif (( pass == 1 )); then 
            printf "with 1 pass in a time of ${quiz_duration} seconds.\n "
        elif (( pass > 1 )); then
            printf "with ${pass} passes in a time of ${quiz_duration} seconds.\n"
        fi
    fi
fi

exit 

測驗本身正在運行,包含隨機問題:

在此輸入影像描述

相關內容