expect遍历密码形式设置用户密码【含sh嵌入expect和纯expect两种方式(对应不同场景)】

2023-10-17 13:14:45

说明

  • 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、" 需转义为\\\"

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中使用\$就为\\\$了。

  • 作者:/*守护她的笑容
  • 原文链接:https://cuichongxin.blog.csdn.net/article/details/119728233
    更新时间:2023-10-17 13:14:45