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

三、基本命令

  1. send:向进程发送字符串,用于模拟用户的输入,但不支持换行 一般需要加上 \r

  2. expect:内部命令

    ​ 判断上次输出结果里是否包含指定的字符串,有则返回,反之就等待超时时间后返回。

    ​ 只能捕捉由spawn启动的进程的输出。

  3. spawn:启动进程,并跟踪后续交互信息。

  4. 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, 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 ~]# 

2)添加用户与用户密码的免交互操作过程

[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匹配。

​ 务必结合上述的案例自己多使用体会理解!


名称栏目:shell编程之Expect免交互
网页路径:http://ybzwz.com/article/gedhjj.html