文章目录
说明
expect遍历密码形式设置用户密码,这个标题可能有点难理解,但需求就是这么个意思:
比如我有100台主机,100台主机里面均需要新建一个用户,如xz_tyng
,并且每个主机的用户密码不一样,就是实现这样的需求,我应该说明白了吧。同样需求可能面临2种情况:
- 一种是限制了root直登
- 一种是没有限制root直登【或者已配置免密】
sh结合expect【适合免密或能root直登的形式】
代码说明
- 如果没有设置root直登,那么们就可以在sh中嵌入expect,然后结合数组遍历文件参数来使用,代码难度稍微提高,但这种代码量很少,且可以直接在另外一个文件中定义ip和密码,很方便。
代码如下【代码我不做解释,如果看不懂,就不要执行,先去学习expect直到能看懂我的代码为止】
[root@etcd1 ccx]# cat user.sh#!/bin/shfile=iplist.txtcat$file|whileread linedoa=($line)
/usr/bin/expect<<EOF
spawn ssh root@${a[0]}
#如果不是免密的,把下面4行注释取消,数组后面1是密码,如果root密码不是这个,可以在文件中后面再加一行,将下面数组的1改为2
#expect {
#"*yes/no" { send "yes\r"; exp_continue}
#"*password:" { send "${a[1]}\r" }
#}
expect "#"
send "useradd xz_tyng\r"
#send "echo 'Xz\^yuN12\#\\\$25' | passwd --stdin xz_tyng\r"
send "echo '${a[1]}' | passwd --stdin xz_tyng\r"
send "exit\r"
expect eof
EOFdone[root@etcd1 ccx]#[root@etcd1 ccx]# cat iplist.txt192.168.59.157 Xz\^yuN12\#\\\$25192.168.59.158 Xz\^yuN12\#\\\$26[root@etcd1 ccx]#
脚本执行并测试
- 执行方式:
sh user.sh
代码本来是在生产环境中执行的,为了ip保护,我把代码放到测试机上执行了【这台测试机是中文环境,和英文环境提示内容稍有区别,但不会影响脚本效果】
[root@etcd1 ccx]# sh user.sh
spawnssh root@192.168.59.157
Last login: Mon Aug1611:01:342021 from192.168.59.156[root@etcd2 ~]# useradd xz_tyng
useradd:用户“xz_tyng”已存在[root@etcd2 ~]# echo 'Xz^yuN12#$25' | passwd --stdin xz_tyng
更改用户 xz_tyng 的密码 。
passwd:所有的身份验证令牌已经成功更新。[root@etcd2 ~]# exit
登出
Connection to192.168.59.157 closed.
spawnssh root@192.168.59.158
Last login: Mon Aug1611:01:342021 from192.168.59.156[root@etcd3 ~]# useradd xz_tyng
useradd:用户“xz_tyng”已存在[root@etcd3 ~]# echo 'Xz^yuN12#$26' | passwd --stdin xz_tyng
更改用户 xz_tyng 的密码 。
passwd:所有的身份验证令牌已经成功更新。[root@etcd3 ~]# exit
登出
Connection to192.168.59.158 closed.[root@etcd1 ccx]#[root@etcd1 ccx]# ssh xz_tyng@192.168.59.158
xz_tyng@192.168.59.158's password:[xz_tyng@etcd3 ~]$[xz_tyng@etcd3 ~]$[xz_tyng@etcd3 ~]$exit
登出
Connection to192.168.59.158 closed.[root@etcd1 ccx]#[root@etcd1 ccx]#
纯expect【适合限制root登陆】
代码说明
- 限制root直登,流程就变了,需要先使用普通用户登陆到主机上,然后su 到root,再执行用户创建和密码修改
这种就只能用纯expect脚本来实现了,expect比较特殊,不支持数组,所以代码量就变多了。【至于为什么不定义变量,没意义,这个不能以便利的形式存在,我在脚本前面定义变量然后在后面调用 和我直接在代码中使用变量内容是一样的,并不能省事】
代码如下【代码我不做解释,如果看不懂,就不要执行,先去学习expect直到能看懂我的代码为止】
[root@etcd1 ccx]# cat expe.sh#!/bin/bash/expect# 变量定义如下,使用在下面send中前面加上$即可#set pswd "123456"
spawnssh teamsun@192.168.59.157expect{"*assword"{ send"w21\#\$\R\T6\r";}"yes/no"{send"yes\r"exp_continue}}expect"etcd"{send"su -\r"}expect{#"*assword" {send "redhat\r";}"密码"{send"redhat\r";}"yes/no"{send"yes\r"exp_continue}}expect"*]#"{send"useradd xz_tyng\r"}expect"*]#"{send"echo 'Xz\^yuN12\#\$37' | passwd --stdin xz_tyng\r"}expect"*]#"{send"exit\r"}expect"etcd"{send"exit\r"}expect eof
spawnssh teamsun@192.168.59.158expect{"*assword"{ send"w21\#\$\R\T6\r";}"yes/no"{send"yes\r"exp_continue}}expect"etcd"{send"su -\r"}expect{#"*assword" {send "redhat\r";}"密码"{send"redhat\r";}"yes/no"{send"yes\r"exp_continue}}expect"*]#"{send"useradd xz_tyng\r"}expect"*]#"{send"echo 'Xz\^yuN12\#\$38' | passwd --stdin xz_tyng\r"}expect"*]#"{send"exit\r"}expect"etcd"{send"exit\r"}#...每一台主机就复制上面的15行代码,改IP就行。# 注,上面的etcd是主机名,需要根据情况修改[root@etcd1 ccx]#
脚本执行并测试
- 执行方式:
expect expe.sh
代码本来是在生产环境中执行的,为了ip保护,我把代码放到测试机上执行了【这台测试机是中文环境,和英文环境提示内容稍有区别,但不会影响脚本效果】
[root@etcd1 ccx]# expect expe.sh
spawnssh teamsun@192.168.59.157
teamsun@192.168.59.157's password:
Last login: Mon Aug 16 11:22:31 2021 from 192.168.59.156
[teamsun@etcd2 ~]$ su -
密码:
上一次登录:一 8月 16 11:22:31 CST 2021pts/1 上
[root@etcd2 ~]# useradd xz_tyng
useradd:用户“xz_tyng”已存在
[root@etcd2 ~]# echo 'Xz^yuN12#$37' | passwd --stdin xz_tyng
更改用户 xz_tyng 的密码 。
passwd:所有的身份验证令牌已经成功更新。[root@etcd2 ~]# exit
登出[teamsun@etcd2 ~]$exit
登出
Connection to192.168.59.157 closed.
spawnssh teamsun@192.168.59.158
teamsun@192.168.59.158's password:
[teamsun@etcd3 ~]$ su -
密码:
上一次登录:一 8月 16 11:09:02 CST 2021从 192.168.59.156pts/1 上
[root@etcd3 ~]# useradd xz_tyng
useradd:用户“xz_tyng”已存在
[root@etcd3 ~]# echo 'Xz^yuN12#$38' | passwd --stdin xz_tyng
更改用户 xz_tyng 的密码 。
passwd:所有的身份验证令牌已经成功更新。[root@etcd3 ~]# exit
登出[teamsun@etcd3 ~]$[root@etcd1 ccx]#[root@etcd1 ccx]#[root@etcd1 ccx]# ssh xz_tyng@192.168.59.158
xz_tyng@192.168.59.158's password:
Permission denied, please try again.
xz_tyng@192.168.59.158's password:
Last failed login: Mon Aug1611:23:42 CST2021 from192.168.59.156 on ssh:notty
There was1 failed login attempt since the last successful login.
Last login: Mon Aug1611:09:232021 from192.168.59.156[xz_tyng@etcd3 ~]$[xz_tyng@etcd3 ~]$exit
登出
Connection to192.168.59.158 closed.[root@etcd1 ccx]#
关于\的使用说明
说明
- 不知道你有没有注意,上面2个脚本中,
$
符号前面的\
这个数量是不一样的。 sh中我用了3个\
,而纯expect中我只用了一个\
。
密码中含有$
是属于特殊情况了,如果密码中能不要$
就别要吧,折腾很。
下面我分开说明这2种情况\
数量的问题,解释之前,我先放expect需要转义的字符串
expect需要转义的字符串
- 下面的
\
、$
和"
毕竟特殊,需要特别留意。- 1、
\
需转义为:\\\
- 2、
}
需转义为\}
- 3、
[
需转义为\[
- 4、
$
需转义为\\\$
- 5、
\
需转义为\` - 6、
"
需转义为\\\"
- 1、
sh中expect的3个\说明
没啥好说明的,expect中的固定格式,如果$
前面你不加3个\
,那么这个转移符就不好使,系统就会识别成如下规则【sh环境是不能识别\$
的】。
并且,如果不加3个\
的话,报错内容如下
【当时这个问题花费了我很多时间,做了很多测试才搞定,你别犟原理,加3个\
就是了】
[root@controller01 ccx]# sh user.sh
spawnssh root@1
Last login: Mon Aug16 09:28:272021 from controller01
Authorizedusers only. All activity may be monitored and reported[root@compute21 ~]# can't read "25": no such variablewhile executing"send "echo'Xz\^yuN12\#\$25'|passwd --stdin xz_tyng\r""
纯expect中的1个\
说明
理论上,这个里面的$
前面也需要加3个\
才对,但我加了一个也没给我报错,我的理解是这样的,sh中调用的expect,它的环境变量2者结合了,,sh识别不到\$
,所以需要完整的格式\\\$
。而纯expect中,环境变量没有涉及到sh的变量,前面说过,sh环境才不能识别到\$
,并没有相关资料说expect不能识别\$
,所以在纯expect中使用\$
就为\\\$
了。