shell编程之Expect免交互
shell编程之Expect免交互
一、前言
shell脚本存在的核心意义就在于基于shell命令简化甚至省略可避免的人工操作,通过各种控制流程结构以及正则表达式等方法,逐步实现自动化操作的整个过程,由此也可见,shell并没有面向对象的思想,类似C语言,毕竟C语言是操作系统或者说是内核的核心语言。
所以,语言并无优劣之分,只是每个人使用的习惯与方式不同,换句话说,难易的不是语言,而是思想与突如其来的灵感。
二、Expect概述与安装
Expect概述
Expect是建立在TCL基础上的一个工具,Expect是用来进行自动化控制和测试的工具。主要解决shell脚本中不可交互的问题。对于大规模的Linux运维很有帮助。
在Linux运维和开发中,我们经常需要远程登录服务器进行操作,登录的过程是一个交互过程,需要输入yes/no password等信息。为了模拟这种输入,可以使用Expect脚本。
Expect的安装:yum install -y expect
三、基本命令
send:向进程发送字符串,用于模拟用户的输入,但不支持换行 一般需要加上 \r
expect:内部命令
判断上次输出结果里是否包含指定的字符串,有则返回,反之就等待超时时间后返回。
只能捕捉由spawn启动的进程的输出。
spawn:启动进程,并跟踪后续交互信息。
- interact:执行完成后保持交互状态,把控制权交给控制台。
Timeout:指定超时时间,过期则继续执行后续指令
- 单位是s
- timeout -1为永不超时
- 默认情况下timeout 是10s
exp_continue--允许expect继续向下执行指令(比较关键,多次交互)
send_user--回显命令,相当于echo
四、expect语法
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"} }
五、实例:
1)ssh免交互远程登录
[root@lokott ~]# yum install -y expect已加载插件:fastestmirror, langpacksLoading 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.129The 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)? yesWarning: 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.129root@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=$1passwd=$2#嵌入写入expect脚本内容??实现免交互的具体内容/usr/bin/expect<<-EOFspawn ssh root@$hostnameexpect { "(yes/no)" {send "yes\r";exp_continue} "*password" {send "$passwd\r";}}#下两行是为了返回本地控制台的操作演示expect "*]#"send "exit\r"expect eofEOF#注意EOF的前后都不可以有空格!!![root@lokott shell]# ./b.sh 192.168.68.129 123456spawn ssh root@192.168.68.129root@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/expectset timeout 5set hostname [lindex $argv 0]set password [lindex $argv 1]spawn ssh root@$hostnameexpect {"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"}}interactexit[root@lokott shell]# ./ssh.sh 192.168.68.133 123456 //服务器非在线spawn ssh root@192.168.68.133ssh: connect to host 192.168.68.133 port 22: No route to host[root@lokott shell]# ./ssh.sh 123456 //参数输入错误spawn ssh root@123456ssh: connect to host 123456 port 22: Invalid argument[root@lokott shell]# ./ssh.sh 192.168.68.129 123456 //正常登录spawn ssh root@192.168.68.129root@192.168.68.129's password: Last login: Wed Dec 4 15:20:51 2019 from 192.168.68.130[root@localhost ~]#
2)添加用户与用户密码的免交互操作过程
[root@lokott shell]# cat c.sh #!/bin/bashusername=$1password=$2useradd $1[ $? -eq 0 ]&&echo ||exit 1/usr/bin/expect<<-EOFspawn passwd ${username}expect { "密码:" {send "${password}\r";exp_continue} "密码:" {send "${password}\r";}}EOFtail -1 /etc/shadow|awk -F: '{print $1,$2}'userdel -r $1#执行结果如下[root@lokott shell]# ./c.sh zhazhahui 123456spawn passwd zhazhahui更改用户 zhazhahui 的密码 。新的 密码:无效的密码: 密码少于 8 个字符重新输入新的 密码:passwd:所有的身份验证令牌已经成功更新。zhazhahui $6$k.MdzCd3$XMUGf5XQV4sE5RJqcwdEoOkd8UVgWtwcguzi6Md5yXcagYvgyE8GmCjzjxkEB5rXR.IRG9j49c36amzkDIe5l1
六、总结
本文主要介绍了expect实现免交互的作用以及结合实际案例讲解其两种使用方式(直接和嵌入)的具体操作过程。
过程主要分为:基本变量定义(timeout最好最先设置)--spawn启动跟踪--expect匹配--interact交付控制权(没有会退出)或者expect eof 结束expect匹配。
务必结合上述的案例自己多使用体会理解!