一个简单的linux shell程序示例及其说明

 


写一个一般的多项选择的小程序,该程序从一个数据文件中读入问题,显示这些问题给用户,程序还能识别正确选项的编号和错误选项的编号。用户可以随时提交答案并计算得分,然后退出程序。


---- 每个主题都在测验的主目录中对应着一个子目录。主目录的名字存放在环境变量 QUIZDIR 中,该变量的默认值为 ~/games/quiz 。这里给出测试的例子中主目录下含有 art politics engineer 三个子目录 ( 对应于三个主题 )

---- 每个主题下包含一系列问题,每个问题在子目录中以文件的形式存在。

---- 每个问题的第 1 行是提问文字。如果这段文字占有多行,那么在新行前必须加一个反斜杠。第 2 行是一个整数,用来表示该问题对应的选项个数。接下去的若干行就是各种选项。最后给出正确答案。

---- 程序在一个主题目录中显示出所有的问题,无论何时用户都可以通过按下 CONTROL+C 组合键退出检测,退出的同时程序将计算测验的结果。在回答完所有问题后程序将计算结果并退出。

---- 程序每次显示一个主题的所有问题之前将打乱显示的顺序。


写在前面的说明:

games 主目录下不仅含有 3 个子目录作为供选择的主题,还含有文件名为 quiz shell 脚本程序,该脚本是考试小程序运行程序。

art 主题目录下含有文件 mod 和脚本文件 setques.sh ,这两个文件用来在该主题下生成测试用的问题文件,运行 setques.sh 传入的参数是需要生成的问题文件数。


给出完整的测试流程和 shell 代码本身:

>cd ~

>mkdir -p games/quiz

>cd quiz

>mkdir engineer art politics

>cat -n quiz

1 #!/bin/bash

2

3 # remove the #on the following line to turn on debugging

4 #set -o xtrace

5

6 #==============

7 function initialize()

8 {

9 trap 'summarize ; exit 0' INT

10 num_ques=0

11 num_correct=0

12 first_time=true

13 cd ${QUIZDIR:=~/games/quiz} || exit 2

14 }

初始化,将变量 QUIZDIR 的值设置为脚本程序的工作目录,如果没有设置该变量,那么默认就为 ~/games/quiz ;设置捕获 CONTROL+C 信号的信号捕捉命令,即响应 CONTROL+C 信号的处理命令是 'summarize;exit 0'


15

16 #=============

17 function choose_subj()

18 {

19 subjects=($(ls))

20 PS3="Choose a subject for the quiz from the preceding list:"

21 select Subject in ${subjects[*]} ;do

22 if [[ -z "$Subject" ]]

23 then

24 echo "No subject chosen.Bye" ?>2

25 exit 1

26 fi

27 echo $Subject

28 return 0

29 done

30 }

选择主题,利用命令 'ls' 和命令替换 $(ls) ,的输出作为元素 --($(ls))-- 对数组 subjects 赋值;利用 select 结构给用户显示主题名字,并把结果保存到变量 Subject 中,并把主题目录名字写到标准输出上 ( 注意主程序将利用命令替换来使用该输出,作为赋值表达式的右值 ) 。命令替换:利用其中命令的输出来代替整条命令。


31

32 #============

33 function exchange()

34 {

35 temp_value=${questions[$1]}

36 questions[$1]=${questions[$2]}

37 questions[$2]=$temp_value

38 }

39

40 #===========

41 function scramble()

42 {

43 typeset -i index quescount

44 questions=($(ls))

45 quescount=${#questions[*]}

46 ((index=quescount-1))

47 while [[ $index >0 ]];do

48 ((target=RANDOM % index ))

49 exchange $target $index

50 ((index -= 1))

51 done

52 }

数组变量 questions 保存所有问题的名字,注意函数中定义的变量具有全局性,除非利用 typeset 声明为局部变量,所以在 exchange 函数中可以对 questions 中的数组元素进行交换操作。同样对 questions 的赋值也是利用命令替换来完成的; quescount 保存了数组元素的个数;函数让索引变量 index quescount 开始向下计数。对索引的每个值,函数选择一个介于 0~index 之间的随机值 ( 取模操作将随机数对应到 0 index 范围 ) ,然后将索引值和选择的随机值所对应数组位置的元素进行对换。运行 scramble 函数的结果会得到两个全局性的变量 questions( 数组,元素为主题目录下的所有文件名,元素与文件名的对应关系被随机排列,注意一个文件对应着一个问题 ) quescount( 数组元素的个数也就是问题的个数 ) 由于函数运行环境通常与被其调用的环境相同,所以其中的变量是显示被函数和调用它的 shell 共享。


53

54 #===========

55 function ask()

56 {

57 exec 3<$1

58 read -u3 ques || exit 2

59 read -u3 num_opts || exit 2

60 index=0

61 choices=()

62 while (( index < num_opts));do

63 read -u3 next_choice || exit 2

64 choices=("${choices[@]}" "$next_choice")

65 ((index += 1))

66 done

67 read -u3 correct_answer || exit 2

68 exec 3<&-

69

70 if [[ $first_time = true ]];then

71 first_time=false

72 echo -e "You may press the interrupt key at any time to quit./n"

73 fi

74

75 PS3="$ques"

76

77 select answer in "${choices[@]}" ;do

78 if [[ -z "$answer" ]];then

79 echo Not a valid choice.Please choose again.

80 elif [[ "$answer" = "$correct_answer" ]];then

81 echo "Correct!"

82 return 1

83 else

84 echo "No,the answer is $correct_answer."

85 return 0

86 fi

87 done

88 }

ask 函数从参数中读入问题文件,并使用文件的内容显示问题,接受用户输入的答案,同时还要判断答案的正确性,函数参数为问题文件的文件名。

利用文件描述符 3 打开参数 ($1- 问题文件文件名 ) 文件;从其中读入相应问题到变量 ques 中,读入选项到数组 choices 中,利用 select 结构显示选项并接受问题答案到变量 answer 中,利用 select 结构的显示提示符 PS3 显示问题 ques


89

90 #===========

91 function summarize()

92 {

93 echo

94 if (( num_ques == 0 ));then

95 echo "You did not answer any questions"

96 exit 0

97 fi

98

99 ((percent=num_correct*100/num_ques))

100 echo "You answered $num_correct questions correctly,out of /

101 $num_ques total questions."

102 echo "Your score is $percent percent."

103 }

summarize 函数为最后统计结果函数。注意在用户测试的过程中利用 CONTROL+C 退出程序时也将调用该函数,因为初始化利用 trap 命令将其信号处理命令定义为了 'summarize;exit 0' ;注意函数定义变量的全局性 ( 在定义这些函数的 shell 环境中全局有效 ) ,正是因为如此才可顺利的显示测验结果。


104

105 #==========

106 initialize

107

108 subject=$(choose_subj)

109 [[ $? -eq 0 ]] || exit 2

110

111 cd $subject || exit 2

112 echo

113 scramble

114

115 for ques in ${questions[*]};do

116 ask $ques

117 result=$?

118 (( num_ques=num_ques+1 ))

119 if [[ $result == 1 ]];then

120 (( num_correct += 1 ))

121 fi

122 echo

123 sleep ${QUIZDELAY:=1}

124 done

125

126 summarize

127 exit 0

128

quiz 主程序,初始化定义一些变量并设置 INT 信号处理命令;运 choose_subj 命令替换,导致其输出赋值给 subject ,也即是选择的主题目录;利用 cd 命令进入所选择的主题目录;利用函数 scramble 得到主题目录下所有问题文件名的数组 (questions) 和问题文件数 (quescount)( 一个问题文件对应一个问题,利用函数变量的全局性在主程序中使用这两个变量 ) ;利用 for 结构遍历问题文件名数组中的每个文件名,利用函数 ask( 参数为问题文件的文件名 ) 显示问题并得到答案, ask 函数的运行结果 ( 返回值 ) 保存到变量 result 中以便统计测验结果。


>cd art

>cat -n mod

1 4

2 0

3 1

4 2

5 3

>cat -n setques.sh

1 #!/bin/bash

2

3 index=0

4 answer=

5 while [ $index -lt $1 ]

6 do

7 echo "The question is $index:">temp

8 # echo -e "The question is $index:/n">temp

9 cat temp mod > $$.$index

10 # echo -e "/n" >>$$.$index

11 answer=$(( $index & 3))

12 echo $answer >>$$.$index

13 (( index+=1))

14 done

15 rm temp

脚本 setques.sh 用来产生测试用问题文件。问题名,选项,答案!下面是运行后的一个问题文件示例:

>./setques.sh 10

>ls

>cat -n 2868.6

1 The question is 6:

2 4

3 0

4 1

5 2

6 3

7 2

>cd ..

>./quiz

setques.sh 可以完善,例如加入对信号的捕捉,使其利用中断键等形式退出时也能够删除 temp 文件。




 

©️2020 CSDN 皮肤主题: 大白 设计师: CSDN官方博客 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值