shell脚本存在的核心意义就在于基于shell命令简化甚至省略可避免的人工操作,通过各种控制流程结构以及正则表达式等方法,逐步实现自动化操作的整个过程,由此也可见,shell并没有面向对象的思想,类似C语言,毕竟C语言是操作系统或者说是内核的核心语言。
成都创新互联主营江城网站建设的网络公司,主营网站建设方案,重庆App定制开发,江城h5重庆小程序开发搭建,江城网站营销推广欢迎江城等地区企业咨询 所以,语言并无优劣之分,只是每个人使用的习惯与方式不同,换句话说,难易的不是语言,而是思想与突如其来的灵感。
Expect是建立在TCL基础上的一个工具,Expect是用来进行自动化控制和测试的工具。主要解决shell脚本中不可交互的问题。对于大规模的Linux运维很有帮助。
在Linux运维和开发中,我们经常需要远程登录服务器进行操作,登录的过程是一个交互过程,需要输入yes/no password等信息。为了模拟这种输入,可以使用Expect脚本。
send:向进程发送字符串,用于模拟用户的输入,但不支持换行 一般需要加上 \r
expect:内部命令
判断上次输出结果里是否包含指定的字符串,有则返回,反之就等待超时时间后返回。
只能捕捉由spawn启动的进程的输出。
spawn:启动进程,并跟踪后续交互信息。
Timeout:指定超时时间,过期则继续执行后续指令
exp_continue——允许expect继续向下执行指令(比较关键,多次交互)
send_user——回显命令,相当于echo
expect [选项] [ -c cmds ] [ [ -[f|b] ] cmdfile ] [ args ]
选项
-c:从命令行执行expect脚本,默认expect是交互地执行的
示例:expect -c 'expect "\n" {send "pressed enter\n"}
-d:可以输出输出调试信息
示例:expect -d ssh.exp
expect最常用的语法(tcl语言:模式-动作)
单一分支模式语法:
expect “hi” {send “You said hi\n"} 匹配到hi后,会输出“you said hi”,并换行,也可以使用\r
多分支模式语法:
expect "hi" { send "You said hi\n" } \ "hehe" { send “Hehe yourself\n" } \ "bye" { send “Good bye\n" }
匹配hi,hello,bye任意字符串时,执行相应输出.等同如下:
expect { "hi" { send "You said hi\n"} "hehe" { send "Hehe yourself\n"} "bye" { send “Good bye\n"} }
[root@lokott ~]# yum install -y expect
已加载插件:fastestmirror, langpacks
Loading mirror speeds from cached hostfile
...//省略部分
[root@lokott shell]# cat a.sh
#使用which expect查看其位置
#!/usr/bin/expect
#设置超时时间
set timeout 20
log_file test.log
log_user 1
#变量定义
set hostname [lindex $argv 0]
set passwd [lindex $argv 1]
#启动进程,spawn监控
spawn ssh root@$hostname
#匹配条件
expect {
"(yes/no)"
#exp_continue表示继续向下匹配
{send "yes\r";exp_continue}
"*password"
{send "$passwd\r"}
}
#转交权限给控制台
interact
[root@lokott shell]# ./a.sh 192.168.68.129 123456 //第一次登录
spawn ssh root@192.168.68.129
The authenticity of host '192.168.68.129 (192.168.68.129)' can't be established.
ECDSA key fingerprint is SHA256:k/6W9M/dgxVrbMgSx9nIFPGfVgUfLMoIb27ys9ZF+LM.
ECDSA key fingerprint is MD5:26:dd:06:b3:32:bd:d6:a3:2f:7c:66:7d:b9:c0:4b:c4.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '192.168.68.129' (ECDSA) to the list of known hosts.
root@192.168.68.129's password:
Last login: Wed Dec 4 10:24:33 2019 from 192.168.68.1
[root@localhost ~]# exit
登出
Connection to 192.168.68.129 closed.
[root@lokott shell]# ./a.sh 192.168.68.129 123456 //第二次登录上一次登录需要的秘钥已经保存
spawn ssh root@192.168.68.129
root@192.168.68.129's password:
Last login: Wed Dec 4 10:29:42 2019 from 192.168.68.130
[root@localhost ~]# exit
登出
Connection to 192.168.68.129 closed.
上述演示的是直接使用expect脚本实现ssh远程登录的操作过程,而expect脚本也可以被嵌入到普通的脚本文件中,下面给出具体演示:
[root@lokott shell]# cat b.sh
#嵌入expect执行实现免交互登录
#!/bin/bash
#定义普通变量,在该shell脚本中始终有效
hostname=$1
passwd=$2
#嵌入写入expect脚本内容??实现免交互的具体内容
/usr/bin/expect<<-EOF
spawn ssh root@$hostname
expect {
"(yes/no)"
{send "yes\r";exp_continue}
"*password"
{send "$passwd\r";}
}
#下两行是为了返回本地控制台的操作演示
expect "*]#"
send "exit\r"
expect eof
EOF
#注意EOF的前后都不可以有空格!!!
[root@lokott shell]# ./b.sh 192.168.68.129 123456
spawn ssh root@192.168.68.129
root@192.168.68.129's password:
Last login: Wed Dec 4 12:22:23 2019 from 192.168.68.130
[root@localhost ~]# exit
登出
Connection to 192.168.68.129 closed.
推荐使用这种方式,因为在实际使用中一般都是在普通脚本中使用expect的,所有嵌入写入比较方便,比较常用!
直接使用expect免交互的完整脚本如下,有兴趣可以试着改为嵌入写入的代码:
[root@lokott shell]# cat ssh.sh
#!/usr/bin/expect
set timeout 5
set hostname [lindex $argv 0]
set password [lindex $argv 1]
spawn ssh root@$hostname
expect {
"No route to host" exit
"Invalid argument" exit
"Connection refused" exit
"Name or service not known" exit
"to continue" {send "yes\r";exp_continue}
"password:" {send "$password\r"}
}
interact
exit
[root@lokott shell]# ./ssh.sh 192.168.68.133 123456 //服务器非在线
spawn ssh root@192.168.68.133
ssh: connect to host 192.168.68.133 port 22: No route to host
[root@lokott shell]# ./ssh.sh 123456 //参数输入错误
spawn ssh root@123456
ssh: connect to host 123456 port 22: Invalid argument
[root@lokott shell]# ./ssh.sh 192.168.68.129 123456 //正常登录
spawn ssh root@192.168.68.129
root@192.168.68.129's password:
Last login: Wed Dec 4 15:20:51 2019 from 192.168.68.130
[root@localhost ~]#
[root@lokott shell]# cat c.sh
#!/bin/bash
username=$1
password=$2
useradd $1
[ $? -eq 0 ]&&echo ||exit 1
/usr/bin/expect<<-EOF
spawn passwd ${username}
expect {
"密码:"
{send "${password}\r";exp_continue}
"密码:"
{send "${password}\r";}
}
EOF
tail -1 /etc/shadow|awk -F: '{print $1,$2}'
userdel -r $1
#执行结果如下
[root@lokott shell]# ./c.sh zhazhahui 123456
spawn passwd zhazhahui
更改用户 zhazhahui 的密码 。
新的 密码:
无效的密码: 密码少于 8 个字符
重新输入新的 密码:
passwd:所有的身份验证令牌已经成功更新。
zhazhahui $6$k.MdzCd3$XMUGf5XQV4sE5RJqcwdEoOkd8UVgWtwcguzi6Md5yXcagYvgyE8GmCjzjxkEB5rXR.IRG9j49c36amzkDIe5l1
本文主要介绍了expect实现免交互的作用以及结合实际案例讲解其两种使用方式(直接和嵌入)的具体操作过程。
过程主要分为:基本变量定义(timeout最好最先设置)——spawn启动跟踪——expect匹配——interact交付控制权(没有会退出)或者expect eof 结束expect匹配。
务必结合上述的案例自己多使用体会理解!
另外有需要云服务器可以了解下创新互联cdcxhl.cn,海内外云服务器15元起步,三天无理由+7*72小时售后在线,公司持有idc许可证,提供“云服务器、裸金属服务器、高防服务器、香港服务器、美国服务器、虚拟主机、免备案服务器”等云主机租用服务以及企业上云的综合解决方案,具有“安全稳定、简单易用、服务可用性高、性价比高”等特点与优势,专为企业上云打造定制,能够满足用户丰富、多元化的应用场景需求。