shell编程:完成一个简单的不重复抽取且自动重新开始自动抽奖脚本
Posted wowchx
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了shell编程:完成一个简单的不重复抽取且自动重新开始自动抽奖脚本相关的知识,希望对你有一定的参考价值。
文章目录
前言:脚本的目标与分析
脚本的目标:
实现一个类似于上课点名的抽奖脚本,抽过的人在本轮中不会重复出现,直到抽完为止,且抽完所有人后自动开始重新抽取。
脚本的分析:
由目标可知,脚本需要具备的主要功能为以下几点:
1.实现简单的抽奖功能
2.抽奖每轮不重复
3.抽完一轮后自动重新开始
抽奖功能我们可以用随机数RANDOM来实现,假如一个班有50个同学,则在文件内对应50个编号,我们可以用((RANDOM%50 + 1))来随机出这50个编号(RANDOM%50求余可以得出0到49的随机数,我们在此基础上加一即可得到1~50的随机数),而抽到的编号我们可以在另一个文件内记录,之后每次判断随机数是否在抽过的编号文件内出现,如果没有出现则代表这个同学没有被抽过。
而抽完之后自动重新开始这个功能我们可以先判断所有同学是否都被抽到过,只要我们确保每个同学不会被重复选取则当两个文件内容数量相等,则代表着所有同学都被抽过一次了。然后只要我们把抽过名单清空,则可以开始新一轮抽奖。
一、 实现脚本的大致步骤
脚本环境:
centos7 bash 4.2.46(2)-release (x86_64-redha
t-linux-gnu) vim编辑器
step1:文件的建立
我们先创建一个用于测试的名单文件name.txt并往里面输入几个名字
cat >> name.txt <<EOF
> 刘一
> 陈二
> 张三
> 李四
> 王五
> 赵六
> 孙七
> 周八
> 吴九
> 郑十
> EOF
step2:抽奖功能的实现
先完成一个基础的抽奖:
因为名单内的名字都是以行为单位的,我们可以认为,总行数即为总人数,我们可以用cat和wc -l 的组合命令求得总人数,同理可以求出被抽过的人数。我们用num_total来表示总人数。用num_lucky来表示被抽过的人数。
num_total=$(cat name.txt|wc -l) #定义num_total为抽奖的总人数
num_lucky=$(cat lucky.txt|wc -l) #定义num_lucky为被抽过的人数
因此我们需要1~num_total的随机数num,即
num=$((RANDOM%(num_total)+1))
然后我们可以用head和tail语句定位那个随机编号num
cat name.txt|head -$num|tail -1
#假设num=5,则上述输出的是头五行的最后一行,即第五行
我们可以实现一个最基础的抽奖脚本
num_total=$(cat name.txt|wc -l)
num=$((RANDOM%(num_total)+1))
echo 恭喜"$(cat name.txt|head -$num|tail -1)"中奖!
step3:不重复抽取功能的实现
新建一个文件用来存放被抽过同学的编号,在抽取随机数的时候,判断随机数在被抽过同学文件是否出现,如果没出现过则代表改随机数对应编号的同学没被抽取过,然后我们把这个编号写入被抽取过的名单里,然后输出结果。相反的,则我们继续抽取随机数。
while(true)
do
num_total=$(cat $name.txt|wc -l)
num=$(($RANDOM % $num_total + 1 ))
if [[ -z "$(cat lucky.txt|grep $num)" ]]
#用cat lucky.txt|grep $num 过滤出num值,再用-z判断是否存在
then
echo 恭喜"$(cat lucky.txt|head -$num|tail -1)"中奖!
echo $num>>lucky.txt
break
else
continue
fi
done
其中[ -z STRING ] “STRING” 的长度为零则为真,与test -z 相等
这样我们就完成了不重复抽取同学的功能,但是一旦写满将一直不满足if条件会进入else命令,则会进入死循环。
step4:抽完自动重新开始的实现与step3的优化
在step3的实际操作中我们发现当总人数大于10时,我们抽几次后可能会陷入死循环,总是永远抽不到刘一。这是因为当例如1和10这样的数同时存在当10先被抽到并且写入到被抽过名单内,再次执行抽到1时if判断那里grep过滤1的时候同样会将10过滤出来因此程序判定1被抽到过,1不会被写入。而随着数量进一步增多,这个现象会更加频繁,因此我们可以用正则表达式^$num$来唯一表达num值来解决这一问题(^char 以char开头的字符 // char$ 以char结尾的字符)
↑↑↑(就是抽不到刘一)
由一开始的脚本分析我们可以知道当两个文件内容数量相等,则代表着所有同学都被抽过一次了。然后只要我们把抽过名单清空,则可以开始新一轮抽奖。
> lucky.txt #清空抽过的名单
因此我们只要在step3的循环外再加一个判断即可。
if (( $num_total==$num_lucky )) #判断是否全部抽完
then
> lucky.txt #清空抽过的名单并输出抽完提示
echo $"全都抽完啦!请再抽一次重新开始"
break
另外,我们把name.txt改为位置变量$1即可运用在不同文件上
二、用vim完成脚本(version1)
在vim编辑器内完成脚本如下:
#!/bin/bash #声名解释器
touch lucky.txt #新建被抽过人的文件
while(true) #主循环
do
num_total=$(cat $1|wc -l) #定义num_total为抽奖的总人数
num_lucky=$(cat lucky.txt|wc -l) #定义num_lucky为被抽过的人数
if (( $num_total==$num_lucky )) #判断是否全部抽完
then
> lucky.txt #清空抽过的名单并输出抽完提示
echo $"全都抽完啦!请再抽一次重新开始"
break
else
num=$((RANDOM%(num_total)+1)) #定义num为随机数
if [[ -z "$(cat lucky.txt|grep ^$num$)" ]] #判断是否没抽过
then
echo $num>>lucky.txt #将没抽过的人编号输入抽过的名单
echo 恭喜"$(cat $1|head -$num|tail -1)"中奖!
break
else
continue #如果抽过重新开始主循环重新抽取
fi
fi
done #结束
脚本测试:
三、脚本的调试与优化
(1)文件异常的处理办法(version-1.1)
当我们不小心或者是在被抽过名单内加入了一些元素,有时会导致脚本的不可运行
↑↑↑(脚本陷入了死循环)
这是因为在写第一个if判断的时候只考虑了抽过名单数小于或等于总名单的情况,而未考虑大于的情况,在正常使用中的确可以正常运行,而当抽过名单文件陷入异常(大于总名单)时,且当编号在抽过名单中全部出现过后,文件不会得到清空,脚本会一直抽取随机数从而陷入死循环。
因此我们只需要把第一个if判断中的等于改为小于等于即可。
if (( $num_total<=$num_lucky )) #当所有人都被抽过或文件陷入异常时
(2)连续抽奖的办法(vision-1.1的改良与部署)
当我们需要连续多次抽奖时,每抽一个人都需要执行一次脚本。我们希望脚本可以实现可连续抽取,随时退出。
我们只需要把第二个if判断后的break去掉,加入一个交互命令即可。
if [[ -z "$(cat chouguo.txt|grep ^$num$)" ]]
then
echo "$num" >>luckylist.txt
echo "恭喜$(cat $1|head -$num|tail -1)中奖!"
#break
read -p "请敲任意键继续" #把break注释掉,加入一个交互命令
同时我们再给脚本可执行权限同时引入到bin目录下面
chmod +x choujiang.sh
cp choujiang.sh /bin
我们在/root/.bashrc下给脚本定义一个永久别名
刷新文件
source /root/.bashrc
这样我们的脚本就部署好了。
四、脚本的其他思路
上述脚本的思路就是在另一个空文件内做加法,把抽过的人的编号加到那个空文件内去,等到所有人都到那个空文件内去了我们再重新开始。
其实我们也可以先把所有人先复制到另一个文件中,每抽到一个人就用sed命令删除,等到文件为空的时候就说明所有人都被抽取过了,再把名单复制过去又可以重新开始。
事实上第二种的思路实现起来更为方便简洁,就留给大家去思考实现了。
以上是关于shell编程:完成一个简单的不重复抽取且自动重新开始自动抽奖脚本的主要内容,如果未能解决你的问题,请参考以下文章