文件:
1234567
abcdefg
......
现在想要随机抽取5列组成下面的内容,允许重复:
36612
cffab
......
- awk -F '' 'BEGIN{srand();for(i=1;i<=5;i++)a[i]=int(rand()*100%7+1)}{for(i=1;i<=5;i++)printf $a[i];printf RS}' file
[解析]
思路是首先把FS设置为空,随机抽取5列,那么在BEGIN模块定义数组a中1~5的下标对应随机的1~7的值,在读行时把数组a的值(这是个1~7的随机值)作为字段打印出来,这样就实现了随机抽取7个字段中的5个字段重新组成新的行。
rand()函数是随机产生一个0到1之间的保留小数点后6位的小数值,例如0.217788,所以需要乘以100得到21.7788,然后再对7取余,结果是0.7788,int()取整是0了,我们要获得1~7的随机数,所以加1,整个表达式才是 int(rand()*100%7+1) ,其实 int(length*rand()+1) 也是可以得到1~7的随机数的,只是在BEGIN中,length函数还是为0,这样就得到了肯定产生于1~7之间的随机数,问题在于rand()只产生一次,怎么让它滚动起来呢?这里我们还需要srand()函数,括号内没有表达式的话,它会采用当前时间作为随机计数器的种子,这样以秒为间隔,随机数就能滚动随机生成了。最后再对应数组a的随机值作为字段打印出来。
-
#!/bin/bash
-
len="5"
-
while read line; do
-
str=""
-
while [ "${#str}" -lt "$len" ]; do
-
letter="${line:$(($RANDOM%${#line})):1}"
-
str="$str$letter"
-
done
-
echo $str
- done < file
[解析]
同样,这个shell脚本也能实现该功能,首先定义变量 len=5 ,因为只需要5列嘛。然后从file文件中读入一行内容给变量 line ,定义一个长度为0的变量 str ,当 str 变量长度大于等于5了,就不再会继续whlie循环,然后打印该变量,在bash中 ${#var} 就是获取变量的长度,我们再看看怎么实现的随机,该shell的原理是读取 line 变量的随机0~6位置长度为1的字符,环境变量RANDOM,范围是0~32767,RANDOM对7取余的结果是0~6,就能随机抽取长度为7的字符串中的任意一个字符,然后把该字符累计给变量 str ,满足长度5后就打印出该行。
谢谢Tim和shell_HAT的精彩代码。
1)
cat file
a 1
b 2
c 3
d 4
e 5
f 6
g 7
h 8
i 9
要求增加第三列为第二列的随机数:
a 1 8
b 2 6
c 3 7
d 4 4
e 5 3
f 6 9
g 7 1
h 8 5
i 9 2
- awk 'BEGIN{while("sort -k2,2 -R file"|getline)a[++i]=$2}{print $0,a[NR]}' file
[解析]
getline读shell命令是我们的老朋友啦,这次再次又它出场,先利用 sort 命令对第二字段随机排序,然后 getline 读入行,利用数组来保存随机排列的第二字段,在正常的执行中利用 NR 为下标 刚好把随机的值排列成第三字段。
- awk '{a[NR]=$0;b[NR]=$2}END{srand();while(i<NR){n=int(rand()*NR)+1;if(!c[n]++){i++;d[i]=b[n]}};for(i=1;i<=NR;i++)print a[i],d[i]}' file
[解析]
这是利用awk本身的随机函数进行操作,srand()函数以时间为种子,保证每次取的随机数不一样,否则rand()只随机一次。rand()函数是取0~1之间的随机小数,这里乘以NR,取整后会得到 0~(NR-1) 之间的随机整数,再+1,得到 1~NR 之间的随机出现的整数,注意这个数可能是重复的,所以利用了数组c来取重,最后利用数组d来保存这些随机数,最后用for循环输出。总的来说效率比较低,特别是在行数多的情况下,求哈希值,哈希冲撞的机会大大增加,效率就会降低。
2)
cat file
aaaaaaa
bbbbbbb
ddddddd
eeeeeee
将第2-4位的替换成随机数:
a136aaa
b569bbb
d906ddd
e661eee
- awk 'BEGIN{srand(systime())}{r=sprintf("%03d",int(rand()*1000));$0=gensub(/(.).../,"\\1"r"",$0)}1' file
这里注意几个问题,第一是构建三位的随机数,乘以1000取整也不一定能取到3位自然数。所以这里用 sprintf 把取整不足100的用零填充为3位。然后用 gensub 替换字符串。不能使用这样的语句
sub(substr($0,2,3),r,$0) ,因为都是一样的字符串,不能按所想的替换2 3 4位字符。