【Linux】shell抽奖学习笔记 (完成版)

有空白的代码

#!/bin/bash
#bug 1 
#seeds=`cat wx.txt`
#sed method
seeds=`sed -Ee 's/\s/,/g' wx.txt`
#wilie method
#seeds=`while read line; do echo ${line// /..}; done < wx.txt`
count=100
rand(){
        while [ $count -ne 1 ] ; do
        seeds=`for seed in $seeds ;do (( $RANDOM%2==0 )) && echo $seed; done`
        count=`echo "$seeds" | wc -l`
        count=$(($count+0))
done
        echo $seeds
}

rand
代码思想
  • 这段代码是想实现从wx.txt拿出数据,然后最终随机选出一位幸运观众,但是每次运行会概率性出现空白,也就是没有人被选出来的尴尬情况
思路
  • 先读取到wx.txt全部数据,存放在seeds上
  • 给一个计数器count,当seeds的count值等于1,才输出最后seeds的值
  • 新的seeds值在for循环中,使用$RANDOM随机变量,这样,每个读取的名字,都会被随机的保留或者去除掉
  • seeds的值会越来越小,因为新的seeds值都会被$RANDOM随机刷掉
  • 当while循环中,count=1时,seeds的值也剩下一个了(一行)整个循环才会结束,就会打印最终的只有一个名字的seeds值了
具体的技术细节
  • seeds=`cat wx.txt`

    • 反引号的作用是把cat wx.txt命令执行后在的结果赋值给seeds
    • 有个问题,如果wx.txt有人名包含空格,比如"tong tong",最终打印运行完代码,打印的seeds值就是tong,而不是"tong tong"
    • 解决的方法就是替换空格
  • seeds=`sed -Ee 's/\s/,/g' wx.txt`

    • sed中的s//g的方法就可以把空格(\s)替换成",",后面有一个g,是把一行中全部的空格都换成逗号
    • sed -eE是不对的写法,-Ee才是正确的写法
  • seeds=`while read line; do echo ${line// /..}; done < wx.txt`

    • 这个方法暂时还看不懂
  • while [ $count -ne 1 ]

    • 我的实现方式和老师不一样,[] 表示里面只能是数字的对比,所以$count和1都必须是数字才行
    • 老师是使用[[ $count == 1 ]],[[]]表示的是字符串的对比
    • 如果count一开始不赋值,那么count会默认一个非数字的值,所以在while [ $count -ne 1 ]的代码之前,加上count=100,只要count不是1都行
  • seeds=`for seed in $seeds ;do (( $RANDOM%2==0 )) && echo $seed; done`

    • 需要注意这里的执行顺序,是先把for seed in $seeds ;do (( $RANDOM%2==0 )) && echo $seed; done的命令执行了,输出了内容之后,再把内容全部赋值给seeds
    • 下面的执行效果就告诉我们,执行了for((i=0;i<3;i++)); do echo i ;done 屏幕打印了全部的值,然后再把这些值赋给a
    • echo “$a” 才会按照行来打印a的值,echo $a 打印的每个值都用空格隔开显示,只显示一行
[root@VM_0_8_centos shizhan]# for((i=0;i<3;i++)); do echo $i ;done
0
1
2
[root@VM_0_8_centos shizhan]# a=`for((i=0;i<3;i++)); do echo $i ;done`
[root@VM_0_8_centos shizhan]# echo "$a"
0
1
2
[root@VM_0_8_centos shizhan]# echo $a
0 1 2
  • count=`echo "$seeds" | wc -l`
    • 统计seeds值有多少行,一行代表一个用户
  • count=$(($count+0))
    • f 这里是把字符串转化成数字
    • seed=ls,反引号的命令赋值永远都是字符串,就是ls的结果是数字,也是字符串
  • ``` count = `ls````
    • 赋值运算千万不要有空格
  • 为什么会有空白的产生
    • 因为seeds的产生,是通过random来的,当seeds只有2个名字,radmon都是奇数,那么seeds的值就等于0了,但即使seeds的值等于0,经过 wc -l之后,这个值还是为1,符合while的条件判断,就不运行了

修复空白的代码

#!/bin/bash
rand(){
        #local count
        #local seeds
        seeds=`sed -Ee 's/\s/,/g' wx.txt`
        count=0
        while [ $count -ne 1 ] ; do
                seeds=`for seed in $seeds ;do (( $RANDOM%2==0 )) && echo $seed; done`
                count=`echo "$seeds" | wc -l`
                count=$(($count+0))
        done
        if [[ $seeds == "" ]];then
                rand
        else
                echo $seeds
        fi
}

rand

思路和技术

  • 加一个判断,当seeds的值为空,就再次递归使用rand函数的,直到seeds不为空,就输出seeds的值出来

去除重复,并手动输出多个中奖号码

#!/bin/bash
rand(){
        seeds=`sed -Ee 's/\s/,/g' wx.txt`
        count=0
        while [ $count -ne 1 ] ; do
                seeds=`for seed in $seeds ;do (( $RANDOM%2==0 )) && echo $seed; done`
                count=`echo "$seeds" | wc -l`
                count=$(($count+0))
        done
        if [[ $seeds == "" ]];then
                rand
        else
                echo $seeds
        fi
}

res(){
        for((i=0;i<10;i++));do
                tmp=`rand`
                while [[ `is_repeat $tmp` == 0 ]];do
                        tmp=`rand`
                done
                arrs[$i]=$tmp
        done
        echo ${arrs[@]}
}

is_repeat(){
        for arr in ${arrs[@]};do
                if [[ $1 == $arr ]];then
                        echo 0
                        return 0
                fi
        done
                                            

es

思路
  • res函数是主函数,10个表示抽取10个人出来
  • is_repeat函数是判断当前需要添加的新值,在seeds中是否有重复的,没有重复才允许添加进去
具体的技术细节
  • for((i=0;i<10;i++));do
    • 表示我们要抽10个人,循环10次,一次取一个人出来
  • is_repeat $tmp是看看tmp的值是否在arrs里面,如果不在,就可以添加到arrs里面,如果在,就循环获取tmp的值,直到tmp的值渣arrs数组中是唯一的
while [[ `is_repeat $tmp` == 0 ]];do
                        tmp=`rand`
                done
                arrs[$i]=$tmp
  • 遍历获取到当前arr的值,$1是指is_repeat $tmp的$tmp,如果tmp在数值,整个函数的结果就返回0了,
        for arr in ${arrs[@]};do
                if [[ $1 == $arr ]];then
                        echo 0
                        return 0
                fi

参数化

res(){
        for((i=0;i<$1;i++));do
                tmp=`rand`
                while [[ `is_repeat $tmp` == 0 ]];do
                        tmp=`rand`
                done
                arrs[$i]=$tmp
        done
        echo ${arrs[@]}
}

res 4
思路
  • 就是之前i<$10的值变成了i<$1,最后通过运行res 4,来决定抽取的人数,4表示抽取4个人

很棒! :+1:

谢谢佳茵小姐姐的鼓励呀