批量创建10个用户stu01-stu10,并且设置随机8位密码,要求:不能用shell循环(例如:for,while等),只能用linux命令及管道实现。

   此题考察的是基础命令的熟练运用,因此,限制了使用shell循环。

                              ------------题目来自老男孩Linux培训博客

  题目看似非常简单,实际上,对Linux基础命令要求非常高,不能使用shell循环就是为了考同学们的基础知识的。

话不多说,言归正传,说实话如果单纯为了到达这道题的结果,只要基础命令好的同学,也很简单,我先说一下我看到题目的思路吧。


1、用户名称的批量生成。  

2、随机密码生成的方法。

3、因为要一条命令搞定,所以考虑构造一个命令,使之符合要求,最后交给bash执行。


思路明确之后,那我们就开始着手做吧。

<1> 用户名称的批量生成

因为我们要生成类似如下的序列:

stu01

stu02

stu03

......

stu08

stu09

stu10

法一:花括号展开{..}


# echo stu{01..10}

stu01 stu02 stu03 stu04 stu05 stu06 stu07 stu08 stu09 stu10

因为我们需要的是竖行排列的,那怎么办呢?

这里说明一下为什么非要竖行排列的,因为我们后面构造命令,是期望一行一行的执行,所以需要让它竖列排序的。

那我们就想办法呀,看看有什么命令能搞定或者echo本身能不能搞定啊。

于是我们又有了两个思路:

①通过echo本身可以打印出竖列的效果,但是此处似乎用不上啊。

# echo -e "stu01\nstu02"

stu01

stu02

②想办法把echo打印出来的序列中的空格替换成回车不就行了嘛,于是,我们想到了tr命令。

[root@oldboylinux ~]# echo -e stu{01..10}|tr " " "\n"
stu01
stu02
stu03
stu04
stu05
stu06
stu07
stu08
stu09
stu10


法二:利用seq命令,seq本身就是干这活的呀,是不是,哈哈。

[root@oldboylinux ~]# seq 10
1
2
3
4
5
6
7
8
9
10


似乎不是我们想要的结果呀,我们想要的是

01

02

...

09

10

那怎么办啊,想想老师的运维思路,你想要的结果,也许就是写程序的人想要的结果,也就是说很可能有类似参数可以用啊,结果一查帮助,果然,而且还有意外的收获。

①seq的"-w"参数,就是专门生成类似序列需求而生的。

②seq的"-f"参数,可以格式化输出序列,我去这不正是我们想要的效果嘛,赶紧试试看。

[root@oldboylinux ~]# seq -w 5  
1
2
3
4
5
[root@oldboylinux ~]# seq -w 05
01
02
03
04
05
[root@oldboylinux ~]# seq -w 10
01
02
03
04
05
06
07
08
09
10


如果我们单纯只是用"-w"参数,那还需要配合其他命令才能达到我们想要的这样的效果。


stu01

stu02

stu03

......

比如:用sed替换

# seq -w 10|sed 's#.*#stu&#g'
stu01
stu02
stu03
stu04
stu05
stu06
stu07
stu08
stu09
stu10


但是我们"man seq"不是看到还有"-f"参数嘛,那还不赶紧试试看。

# seq -f "stu%02g" 10 
stu01
stu02
stu03
stu04
stu05
stu06
stu07
stu08
stu09
stu10


解释一下,后面引号里面的内容,这个是与C语言printf格式化输出类似,%号前面指定字符串,后面的02g中的2表示输出列宽为2列,0表示用0填充不足的部分,默认是用空格填充不足的部分,g是表示整数输出。

好了,序列我们有了,下一个就是想办法生成8位随机密码了。

<2> 8位随机密码的生成

  Linux系统下生成随机密码的方法有很多,我们最容易想到的就是$RANDOM,这是一个由系统自动维护的随机数。我们参考网络资料,简单介绍其中的四种方法供大家参考。

          来源:http://www.cnblogs.com/chengmo/archive/2010/10/23/1858879.html

①通过时间获得随机数(date)


这个也是我们经常用到的,可以说时间是唯一的,也不会重复的,从这个里面获得同一时间的唯一值。适应所有程序里面了。

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
[chengmo@centos5  shell]$ date +%s
1287764773
#获得时间戳,当前到:1970-01-01 00:00:00 相隔的秒数
#如果用它做随机数,相同一秒的数据是一样的。在做循环处理,多线程里面基本不能满足要求了。
 
[chengmo@centos5  shell]$ date +%N
738710457
#获得当前时间的纳秒数据,精确到亿分之一秒。
#这个相当精确了,就算在多cpu,大量循环里面,同一秒里面,也很难出现相同结果,不过不同时间里面还会有大量重复碰撞
 
[chengmo@centos5  shell]$ date +%s%N
1287764807051101270
#这个可以说比较完美了,加入了时间戳,又加上了纳秒

 

通过上面说明,用它来做随机数的基数了,接下来我们看怎么样获得一段数据内怎么样获得随机数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#!/bin/sh
 
#写个随机函数,调用方法random min max
#在min 与 max直接获得随机整数
#copyright chengmo QQ:8292669
 
 
#获得随机数返回值,shell函数里算出随机数后,更新该值
function random()
{
    min=$1;
    max=$2-$1;
    num=$(date +%s+%N);
    ((retnum=num%max+min));
    #进行求余数运算即可
    echo $retnum;
    #这里通过echo 打印出来值,然后获得函数的,stdout就可以获得值
    #还有一种返回,定义全价变量,然后函数改下内容,外面读取
}
 
#得到1-10的seq数据项
for i in {1..10};
do 
    out=$(random 2 10000);
    echo $i,"2-10000",$out;
done;



②通过内部系统变量($RANDOM)


其实,linux已经提供有个系统环境变量了,直接就是随机数,哈哈,觉得刚学习方法,是不是白费了!!

1
2
3
4
5
6
[chengmo@centos5  shell]$ echo $RANDOM
10918
[chengmo@centos5  shell]$ echo $RANDOM
10001
 
#连续2次访问,结果不一样,这个数据是一个小于或等于5位的整数

可能有疑问了,如果超过5位的随机数怎么得到呢?

呵呵,加个固定10位整数,然后进行求余,跟例1 一样了。接下来的例子又是我们自立更生做了。



③通过系统内部唯一数据生成随机数(/dev/random,urandom)


我们知道dev目录下面,是linux一些默认设备,它给我们感觉就是放的是键盘,硬盘,光驱等设备的对应文件了。 其实linux有些设备很特殊,有特殊用途。前面我们说到的:/dev/[udp|tcp]/host/port比较特殊吧。呵呵,有扯远了。

/dev/random设备,存储着系统当前运行的环境的实时数据。它可以看作是系统某个时候,唯一值数据,因此可以用作随机数元数据。我们可以通过文件读取方式,读得里面数据。/dev/urandom这个设备数据与random里面一样。只是,它是非阻塞的随机数发生器,读取操作不会产生阻塞。


得到整型数据,然后,类似一的方法就可以获得到随机数了。 题外话:在程序里面,我们经常md5得到唯一值,然后是字符串的,如果想表示成整型方式,可以通过crc函数.crc是循环冗余校验,相同数据通过运算,都会得到一串整型数据。现在这种验证应用很广。详细要了解,可以参考:crc.


④读取linux 的uuid码

 

在提到这个之前,有个概念,就是什么是uuid呢?

UUID码全称是通用唯一识别码 (Universally Unique Identifier, UUID),它 是一个软件建构的标准,亦为自由软件基金会 (Open Software Foundation, OSF) 的组织在分布式计算环境 (Distributed Computing Environment, DCE) 领域的一部份。

 

UUID 的目的,是让分布式系统中的所有元素,都能有唯一的辨识信息,而不需要通过中央控制端来做辨识信息的指定。如此一来,每个人都可以创建不与其它人冲突的 UUID。在这样的情况下,就不需考虑数据库创建时的名称重复问题。它会让网络任何一台计算机所生成的uuid码,都是互联网整个服务器网络中唯一的。它的原信息会加入硬件,时间,机器当前运行信息等等。

UUID格式是:包含32个16进位数字,以“-”连接号分为五段,形式为8-4-4-4-12的32个字符。范例;550e8400-e29b-41d4-a716-446655440000  ,所以:UUID理论上的总数为216 x 8=2128,约等于3.4 x 1038。 也就是说若每奈秒产生1兆个UUID,要花100亿年才会将所有UUID用完。

其实,大家做数据库设计时候,肯定听说过,guid(全局唯一标识符)码,它其实是与uuid类似,由微软支持。 这里编码,基本有操作系统内核产生。大家记得把,在windows里面,无论数据库,还是其它软件,很容易得到这个uuid编码。

 

linux 的uuid码

linux的uuid码也是有内核提供的,在/proc/sys/kernel/random/uuid这个文件内。其实,random目录,里面还有很多其它文件,都与生成uuid有关系的。

1
2
3
4
5
6
7
8
9
[chengmo@centos5 ~/shell]$ cat /proc/sys/kernel/random/uuid
dff68213-b700-4947-87b1-d9e640334196
[chengmo@centos5 ~/shell]$ cat /proc/sys/kernel/random/uuid
7b57209a-d285-4fd0-88b4-9d3162d2e1bc
#连续2次读取,得到的uuid是不同的
 
[chengmo@centos5 ~/shell]$ cat /proc/sys/kernel/random/uuid| cksum | cut -f1 -d" "
2141807556
#同上方法得到随机整数

我们用的是方法是第二种,然后结合md5sum,最后取出8位作为密码,因为这样获取的密码是字符加数字

[root@oldboylinux ~]# echo $RANDOM|md5sum|cut -c 1-8
d8c57fd6
[root@oldboylinux ~]# echo $RANDOM|md5sum|cut -c 1-8
b6e693b6
[root@oldboylinux ~]# echo $RANDOM|md5sum|cut -c 1-8
6dbb7cac
[root@oldboylinux ~]# echo $RANDOM|md5sum|cut -c 1-8
663bda06


好了,到这里为止,我们前两步都有办法解决了,那就是最后一步了,把他们拼凑成我们想要的命令效果,然后交个bash执行就可以了。

<3> 拼凑想要的语句效果提交给bash执行

#seq -f "stu%02g" 10|sed -nr 's#(.*)#useradd \1;random=$(echo $RANDOM|md5sum|cut -c 1-8);echo "$random"|passwd --stdin \1#gp'|bash -x &>useradd.txt 

好了,我们来分开解释一下每一部分的作用。

seq -f "stu%02g" 10 

这一部分就是用于批量生成用户名;

sed -nr 's#(.*)#useradd \1;random=$(echo $RANDOM|md5sum|cut -c 1-8);echo "$random"|passwd --stdin \1#gp'

这一段,就是用sed的替换功能来拼凑我们想要的命令结果,拼凑的结果类似于如下结果,

\1表示前面(.*)内匹配到的字符串;

 useadd stu01;random=$(echo $RANDOM|md5sum|cut -c 1-8);echo "$random"|passwd --stdin stu01

bash -x &>useradd.txt

这一段,按道理说,只要有"bash"就可以了,结果肯定会执行了,但是我们想一下,是不是哪里还有点小问题啊,那就是我们自己创建的用户,密码是什么呢?你不会告诉我,你也不知道吧,哈哈,一开始我就是干的呀,后面想测试一下能不能用创建的用户登陆系统时,发现完蛋,不知道登录密码,所以想再交个bash执行前,把内容输出一份到文件,可是前面没有执行啊,哪里有密码呢,一度以为这条路是必定有瑕疵了,后来又转念一想,bash不是有调试参数嘛,OK,那就试试,结果很不错,可是又无法追加到文件,奇怪呀,原来调试信息是当错误信息来处理的呀,那就来个绝的,管你是标准输出还是错误输出都给我到指定文件去,这下就OK了, 完美。


你是不是以为到这里就结束了,哼,才不会呢!继续往下看哦,更多精彩还在后面呢。。。


拼凑方法大总结:

①sed替换大法,即sed 's#要替换的内容#替换后的内容#g',例子见上面和最后面的案例。

②awk拼凑大法,例子如下:

#echo -e "$(echo $RANDOM|md5sum|cut -c 1-8)\n$(echo $RANDOM|md5sum|cut -c 1-8)\n$(echo $RANDOM|md5sum|cut -c 1-8)\n$(echo $RANDOM|md5sum|cut -c 1-8)\n$(echo $RANDOM|md5sum|cut -c 1-8)\n$(echo $RANDOM|md5sum|cut -c 1-8)\n$(echo $RANDOM|md5sum|cut -c 1-8)\n$(echo $RANDOM|md5sum|cut -c 1-8)\n$(echo $RANDOM|md5sum|cut -c 1-8)\n$(echo $RANDOM|md5sum|cut -c 1-8)"|awk '{print "echo "$1"|""passwd --stdin stu0"NR}'|awk '{print "useradd " $NF";"$0}'|tee useradd.txt|bash


1、echo -e "$(echo $RANDOM|md5sum|cut -c 1-8)\n$(echo $RANDOM|md5sum|cut -c 1-8)\n$(echo $RANDOM|md5sum|cut -c 1-8)\n$(echo $RANDOM|md5sum|cut -c 1-8)\n$(echo $RANDOM|md5sum|cut -c 1-8)\n$(echo $RANDOM|md5sum|cut -c 1-8)\n$(echo $RANDOM|md5sum|cut -c 1-8)\n$(echo $RANDOM|md5sum|cut -c 1-8)\n$(echo $RANDOM|md5sum|cut -c 1-8)\n$(echo $RANDOM|md5sum|cut -c 1-8)"

这一大段,看着好吓人啊,其实就是一个目的重复10次,你分开看就是这样的

$(echo $RANDOM|md5sum|cut -c 1-8)\n】就是用来生成8位随机密码,重复10次,还记得最开始的echo -e参数换行大法嘛,没错,这里就用上了。

2、awk '{print "echo "$1"|""passwd --stdin stu0"NR}'|awk '{print "useradd " $NF";"$0}'

这一行就是用来拼凑我们想要实现的效果语句,具体类似如下:


    useradd stu01;echo 1b487225|passwd --stdin stu01


3、tee useradd.txt

这一行就是为了保存生成密码和用户名到文件useradd.txt

4、bash

把上面拼凑的语句交给bash,执行。


其他拼凑方法参考:

  基本上都是类似上述思路,只是实现方法有所不同罢了,各位自己斟酌吧,这些都搞定,说明我们的Linux基础就真OK了,重点关注一下加红的部分的实现思路。


①#echo a2stu{01..02}|xargs -n1|awk '{print"useradd "$0" && mkpasswd -l 8 >>/tmp/mima.txt;tail -1 /tmp/mima.txt|passwd --stdin "$0}'|bash


#echo stu{01..10} | sed "s# #\n#g" | awk '{print "useradd " $1 " -p " "`echo $[RANDOM**3]|cut -c 1-8`"}'|bash


#echo stu{01..10} | sed "s# #\n#g" | awk '{cmd="echo $[RANDOM**3]|cut -c 1-8";print "useradd " $1 " -p " "`echo $[RANDOM**3]|cut -c 1-8`"}' |bash


④echo stu{1..10}|xargs -n 1|awk '{print "useradd", $1,"&& " "echo `date +%N`|cut -c 1-8 |" "passwd --stdin",$1 }'|bash


⑤# echo stu{01..10}|xargs -n 1|sed -n 's#.*#useradd & \&\& echo `date +s%`|cut -c 1-8|passwd --stdin & #gp'|bash


⑥#echo stu{1..10}|xargs -n 1|awk '{print "useradd", $1,"&& " "echo `date +%N`|cut -c 1-8 |" "passwd --stdin",$1 }'|bash 


⑦#echo stu{01..10}|xargs -n1 useradd && echo stu{01..10}:`date +%N|cut -c1-8`|xargs -n1|tee -a pass.txt|chpasswd


⑧#echo user{01..10} | xargs -n1| sed -n 's/.*/useradd &\n echo `&|md5sum|cut -c 1-8`|passwd --stdin &/p'|bash


⑨#echo stu{01..10}|tr " " "\n"|sed -r 's#(.*)#useradd \1;pass=`echo $((RANDOM+123456))|md5sum|cut -c 2-9`;echo $pass|passwd --stdin \1;echo "\1 `echo $pass`" >>/tmp/b.log#g'|bash


⑩#echo stu{01..10}|xargs -n1|sed -nr 's#^(.*)$#useradd \1;echo $(date +%T)|md5sum|cut -c 1-8 >/root/\1.txt;cat /root/\1.txt|passwd --stdin \1#gp' >>/root/useradd.txt 2>&1


#echo stu{01..10}:$(date +%N)|sed 's# #\n#g' >/mnt/pass;awk -F ":" '{print $1}' /mnt/pass |xargs -n 1 useradd;pwunconv;chpasswd </mnt/pass;pwconv


#echo stu{01..10}|tr " " "\n"|sed -r 's#(.*)#useradd \1;lc=$((RANDOM+10000000)); echo "$lc"|passwd --stdin \1#g'|bash


#echo stu{01..10}:$(cut -c 1-8 <<< $(md5sum <<< $RANDOM))|xargs -n1|tee useradd.txt|awk -F ':' '{print "useradd "$1 " && echo " $2 "|passwd --stdin " $1}'|bash


#echo stu{01..10} |tr ' ' '\n'|sed -rn 's@^(.*)$@useradd \1 ; echo $RANDOM|md5sum|cut -c 1-8 >/data/\1;cat /data/\1|passwd --stdin \1@gp'|bash


#$echo stu{01..10}c$[$RANDOM**3] | sed 's# #\n#g' | cut -c 1-14 | awk -F'c' '{print"useradd " $1" && echo "$2" | passwd --stdin "$1}' | bash


#echo stu{1..10}|xargs -n1 useradd ;echo stu{1..10}:`cat /dev/urandom|tr -dc 0-9|fold -w8|head -1`|xargs -n1|tee -a pass.txt|chpasswd