The requirements are as follows:
1) Assume that there are 72 persons in total (providing a list of persons with the name of Chinese Pinyin)
2) Seven groups
3) To ensure the randomness of the personnel distribution group, that is, the results of each execution of the script should be different.
Knowledge Point 1: Generating Random Numbers
In Linux system, there is a built-in variable RANDOM, which is actually a random number. The usage is as follows:
# echo $RANDOM 17023
This RANDOM variable ranges from 0 to 32767. If you want to get a longer random number, you can achieve it by adding multiple RANDOM variables, for example:
# echo $RANDOM$RANDOM 2239928526
The second method is to use the date command. The date has a% N, which can print the second of the current time in nanoseconds:
# date +%N 594253932
The third method is to read the system's special device / dev/urandom, as follows:
# head -100 /dev/urandom|cksum 2604766758 24408
This / dev/urandom is a special device that generates random numbers. It's binary, so you can't cat or head directly, but you can add pipes to deliver the results to cksum. Cksum is a tool for checking files or characters, which generates two numeric strings. Because the string in front of the pipe is random, the number string is also random.
We just need to get the first number string, so we need awk to deal with it.
# head -100 /dev/urandom|cksum|awk '{print $1}' 2194071486
Of course, the cksum command can also calculate the check value of a string to get a random number, as follows:
# echo "Zhangsan"|cksum |awk '{print $1}' 2674179083
Knowledge Point 2: Disorder
Because the case requires randomness, the best way to give the list of people is to disrupt the order, so as to ensure that each script execution has a certain randomness, you can use the shuf command to achieve:
# seq 1 5 |shuf 2 4 5 1 3 # seq 1 5 |shuf 3 4 5 1 2
Each time you get a different sequence. It can also act directly on the file, which will disrupt the line order of the file:
# shuf /etc/passwd |head -n2 user_82:x:1102:100::/home/user_82:/bin/bash user_93:x:1113:100::/home/user_93:/bin/bash # shuf /etc/passwd |head -n2 user_13:x:1033:100::/home/user_13:/bin/bash user_31:x:1051:100::/home/user_31:/bin/bash
Note that the shuf command simply scrambles the line order and does not change the position of the characters in the line.
Knowledge Point 3: Number Rounding in shell
Starting operations in the shell will directly discard decimal numbers, such as 1.9 to 1, which is obviously unreasonable. The following method can achieve rounding.
#!/bin/bash #The following functions are for division operations and limit $1 to dividend and $2 to divide. div() { n=`echo "scale=1;$1/$2" |bc` n1=`echo "sclae=1;$n+0.5" |bc` echo $n1 |cut -d . -f1 } div 10 3
List of testers for this case
# vim member.txt
xiaoguisheng guoyuqing xiongyongzheng mengjintang chaizuzhou zhousheng xufangming zhaoliangyun hanshiru wangxianyi zhangjipei luxiuli yangshugen guoyongzhi lijianguo wuqiongchen dinglin yaoyashan yinzijia wangbencheng liuxiuwen chenzuqi leyuguo baozongyao fenghao sunxiaoquan zhangyaxian lijiuzhe dulichun lixi shenpeiwen zousilin luoping chaiyan fandaozhang huzixiang jinzhen zhujunfeng liqianbiao hangyanliang luorenjian loujianji fujianzhou gengyiwu jinjigui liuzhizhong lisanyan lisili zhangyiyu songguozhen zhangxinghua zhaozhiyong huanghe xiaojie fanhongfei wangguiwen renshumin songfuying zhanghaibo liguangqun puaihua yanzhihua gaojixian liulai funing chenruizhi chendaxin laishaoying xujian xiaozhekou xuxiaping jiangchunqing
Reference script for this case
#!/bin/bash #Divide people into groups #Authors: #Date: #Version: v1.0 #Personnel List File f=member.txt #Number of Groups group_n=7 #Total number of personnel member_n=`wc -l $f|awk '{print $1}'` #Calculate the id of the user's group by name get_n() { #Calculate cksum value by name l=`echo $1|cksum|awk '{print $1}'` #Get a random number n1=$RANDOM #The cksum value is added to the random number and then divided by the number of groups to make sure that the remainder is different each time. n2=$[$n1+$l] g_id=$[$n2%$group_n] #If the number of groups is 7, the remainder range is 0-6, and if the remainder is 0, the group is 7. if [ $g_id -eq 0 ] then g_id=$group_n fi echo $g_id } for i in `seq 1 $group_n` do #n_$i.txt is a temporary document for recording members of the group #If the script has been executed before, the file will exist. The temporary file should be deleted before the script is executed this time. [ -f n_$i.txt ] && rm -f n_$i.txt done shuf $f|while read name do #Calculate the id of the user's group g=`get_n $name` #Adding people to his team echo $name >> n_$g.txt done #Define a function that calculates the number of rows in a file nu(){ wc -l $1|awk '{print $1}' } #Group with the largest number of members max(){ ma=0 for i in `seq 1 $group_n|shuf` do n=`nu n_$i.txt` if [ $n -gt $ma ] then ma=$n fi done echo $ma } #Get the group with the least number of members min(){ mi=$member_n for i in `seq 1 $group_n|shuf` do n=`nu n_$i.txt` if [ $n -lt $mi ] then mi=$n fi done echo $mi } #Define rounding function div() { n=`echo "scale=1;$1/$2"|bc` n1=`echo "scale=1;$n+0.5"|bc` echo $n1|cut -d. -f1 } #Average group membership (non-rounded) ava_n=$[$member_n/$group_n] #Average group membership (rounded) ava_n1=`div $member_n $group_n` if [ $ava_n -eq $ava_n1 ] then #Define the initial minimum ini_min=1 #Here's what the while cycle does, which is to get people from large groups into small groups. #The condition of this while cycle is that the minimum number of group members is less than the average number of group members. while [ $ini_min -lt $ava_n1 ] do #Find out the group with the largest number of people m1=`max` #Find the group with the least number of people m2=`min` for i in `seq 1 $group_n|shuf` do n=`nu n_$i.txt` #Find the file f1 corresponding to the group with the largest number of people (there may be more than one, just take the first one that appears here) if [ $n -eq $m1 ] then f1=n_$i.txt #Find the file f2 corresponding to the group with the least number of people (there may be more than one, just take the first one that appears here) elif [ $n -eq $m2 ] then f2=n_$i.txt fi done #Take the last person's name in f1 name=`tail -n1 $f1` #Add this name to f2 echo $name >> $f2 #Delete the name just taken from f1 sed -i "/$name/d" $f1 #Assign ini_min the minimum number of people at this time ini_min=`min` done else #Define the initial maximum ini_max=$member_n while [ $ini_max -gt $ava_n1 ] do #Find out the group with the largest number of people m1=`max` #Find the group with the least number of people m2=`min` for i in `seq 1 $group_n|shuf` do n=`nu n_$i.txt` #Find the file f1 corresponding to the group with the largest number of people (there may be more than one, just take the first one that appears here) if [ $n -eq $m1 ] then f1=n_$i.txt #Find the file f2 corresponding to the group with the least number of people (there may be more than one, just take the first one that appears here) elif [ $n -eq $m2 ] then f2=n_$i.txt fi done #Take the last person's name in f1 name=`tail -n1 $f1` #Add this name to f2 echo $name >> $f2 #Delete the name just taken from f1 sed -i "/$name/d" $f1 #Assign ini_min the minimum number of people at this time ini_max=`max` done fi for i in `seq 1 $group_n` do echo -e "\033[34m$i The members of the group are:\033[0m" cat n_$i.txt #Delete temporary files rm -f n_$i.txt echo done