如何使用 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++ )))。

回答完最后一个问题后,将对用户的表现进行评估,其中会传达正确、错误答案和通过(如果有)的次数,还会给出所花费的时间。

这是一个简短的解释,但脚本本身有各种注释,应该能使工作原理清晰明了!它可以适用于任何其他类型的测验,例如首都和国家。

将脚本复制到文件中后,使其可执行(chmod u+x),然后运行它./scriptname或将其放在您的~/bin文件夹中,并像任何其他程序一样按名称调用它,假设您的~/binPATH

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

测验本身的实际操作,包含随机问题:

在此处输入图片描述

相关内容