利用expect命令实现Shell自动化交互的方法详解

背景

linux脚本中有很多场景是进行远程操作的,例如远程登录ssh、远程复制scp、文件传输sftp等。这些命令中都会涉及到安全密码的输入,正常使用命令时是需要人工手动输入密码并接受安全验证的。为了实现自动化远程操作,我们可以借用expect的功能。

expect是一个免费的编程工具语言,用来实现自动和交互式任务进行通信,而无需人的干预。expect是不断发展的,随着时间的流逝,其功能越来越强大,已经成为系统管理员的的一个强大助手。expect需要Tcl编程语言的支持,要在系统上运行expect必须首先安装Tcl。

expect的安装

expect是在Tcl基础上创建起来的,所以在安装expect前我们应该先安装Tcl。

(一)Tcl 安装

主页: http://www.tcl.tk

下载地址: http://www.tcl.tk/software/tcltk/downloadnow84.tml

1.下载源码包

wget http://nchc.dl.sourceforge.net/sourceforge/tcl/tcl8.4.11-src.tar.gz 

2.解压缩源码包

tar xfvz tcl8.4.11-src.tar.gz 

3.安装配置

cd tcl8.4.11/unix
./configure --prefix=/usr/tcl --enable-shared
make
make install 

注意:

1、安装完毕以后,进入tcl源代码的根目录,把子目录unix下面的tclUnixPort.h copy到子目录generic中。

2、暂时不要删除tcl源代码,因为expect的安装过程还需要用。

(二)expect 安装 (需Tcl的库)

主页: http://expect.nist.gov/

1.下载源码包

wget http://sourceforge.net/projects/expect/files/Expect/5.45/expect5.45.tar.gz/download 

2.解压缩源码包

tar xzvf expect5.45.tar.gz 

3.安装配置

cd expect5.45
./configure --prefix=/usr/expect --with-tcl=/usr/tcl/lib --with-tclinclude=../tcl8.4.11/generic
make
make install
ln -s /usr/tcl/bin/expect /usr/expect/bin/expect 

expect

expect的核心是spawn、expect、send、set。

spawn 调用要执行的命令

  • expect 等待命令提示信息的出现,也就是捕捉用户输入的提示:
  • send 发送需要交互的值,替代了用户手动输入内容
  • set 设置变量值
  • interact 执行完成后保持交互状态,把控制权交给控制台,这个时候就可以手工操作了。如果没有这一句登录完成后会退出,而不是留在远程终端上。
  • expect eof 这个一定要加,与spawn对应表示捕获终端输出信息终止,类似于if....endif

expect脚本必须以interact或expect eof结束,执行自动化任务通常expect eof就够了。

其他设置

  • 设置expect永不超时 set timeout -1
  • 设置expect 300秒超时,如果超过300没有expect内容出现,则退出 set timeout 300

expect编写语法

expect使用的是tcl语法

  • 一条Tcl命令由空格分割的单词组成. 其中, 第一个单词是命令名称, 其余的是命令参数
    cmd arg arg arg
  • $符号代表变量的值. 在本例中, 变量名称是foo.
    $foo
  • 方括号执行了一个嵌套命令. 例如, 如果你想传递一个命令的结果作为另外一个命令的参数, 那么你使用这个符号
    [cmd arg]
  • 双引号把词组标记为命令的一个参数. "$"符号和方括号在双引号内仍被解释
    "some stuff"
  • 大括号也把词组标记为命令的一个参数. 但是, 其他符号在大括号内不被解释
    {some stuff}
  • 反斜线符号是用来引用特殊符号. 例如:n 代表换行. 反斜线符号也被用来关闭"$"符号, 引号,方括号和大括号的特殊含义

示例

login.exp专用于远程登录,快捷使用方式: login.exp "exclude" "${remote_ip}" "${remote_user}" "${remote_passwd}" "${remote_command}"

#!/usr/bin/expect -f
##########################################################
# 通过SSH登陆和执行命令
#参数:1.Use_Type [check/execute]
#  2.SSHServerIp
#  3.SSHUser
#  4.SSHPassword
#  5.CommandList [多个命令间以;间隔]
#返回值:
#  0 成功
#  1 参数个数不正确
#  2 SSH 服务器服务没有打开
#  3 SSH 用户密码不正确
#  4 连接SSH服务器超时
##########################################################
proc usage {} {
 regsub ".*/" $::argv0 "" name
 send_user "Usage:\n"
 send_user " $name Use_Type SSHServerIp SSHUser SSHPassword CommandList\n"
 exit 1
}
## 判断参数个数
if {[llength $argv] != 5} {
 usage
}
#设置变量值
set Use_Type [lindex $argv 0]
set SSHServerIp [lindex $argv 1]
set SSHUser [lindex $argv 2]
set SSHPassword [lindex $argv 3]
set CommandList [lindex $argv 4]
#spawn ping ${SSHServerIp} -w 5
#expect {
# -nocase -re "100% packet loss" {
#  send_error "Ping ${SSHServerIp} is unreachable, Please check the IP address.\n"
#  exit 1
# }
#}
set timeout 360
set resssh 0
#定义变量标记ssh连接时是否输入yes确认
set inputYes 0
set ok_string LOGIN_SUCCESS
if {$Use_Type=="check"} {
 #激活ssh连接,如果要需要输入yes确认,输入yes,设置inputYes为1,否则输入ssh密码
 spawn ssh ${SSHUser}@${SSHServerIp} "echo $ok_string"
} else {
 spawn ssh ${SSHUser}@${SSHServerIp} "$CommandList"
}
expect {
 -nocase -re "yes/no" {
  send -- "yes\n"
  set inputYes 1
 }
 -nocase -re "assword: " {
  send -- "${SSHPassword}\n"
  set resssh 1
 }
 #-nocase -re "Last login: " {
 #  send -- "${CommandList}\n"
 #}
 $ok_string {}
 -nocase -re "Connection refused" {
  send_error "SSH services at ${SSHServerIp} is not active.\n"
  exit 2
 }
 timeout {
  send_error "Connect to SSH server ${SSHUser}@${SSHServerIp} timeout(10s).\n"
  exit 4
 }
}
#如果输入了yes确认,输入ssh密码
if {$inputYes==1} {
 expect {
  -nocase -re "assword: " {
   send -- "${SSHPassword}\n"
   set resssh 1
  }
 }
}
#如果出现try again或者password:提示,说明输入的用户密码错误,直接退出。
if {$resssh==1} {
 expect {
  -nocase -re "try again" {
   send_error "SSH user:${SSHUser} passwd error.\n"
   exit 3
  }
  -nocase -re "assword:" {
   send_error "SSH user:${SSHUser} passwd error.\n"
   exit 3
  }
  eof {}
 }
}
send_error -- "$expect_out(buffer)"
#-nocase -re "No such user" {
#  send_error "No such user.\n"
#  exit 5
# }
#exit

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对我们的支持。

(0)

相关推荐

  • shell结合expect写的批量scp脚本工具

    在部署一个任务时,其中有一项必须的过程就是将一些文件,如安装包发送到大量的服务器上去.虽然已有宇哥的脚本可用:通过paramiko模块提供的ssh和scp功能编写的python脚本.但我到现在还在对python的恐惧之中(虽然已经在空闲时间努力去学习了),所以使用了shell和expect脚本结合的方式,写了这个批量scp的脚本工具. expect用于自动化地执行linux环境下的命令行交互任务,例如scp.ssh之类需要用户手动输入密码然后确认的任务.有了这个工具,定义在scp过程中可能遇到的

  • shell脚本无密码登录 expect的使用方法详解

    shell脚本无密码登录 expect的使用方法详解 今天需要做一个定时任务脚本将最新的数据包文件传到远程的服务器上,虽然有密钥但也是要求输入密码的那种,所以只能另想办法实现让脚本自动输入密码了. 从网上查到使用expect可以,简单研究了一下,效果不错. 因为我的操作系统没有安装expect,所以直接"yum -y install expect",你可以根据你的操作系统安装expect,或者源码编译. 安装好之后就可以使用了,这里有几种方法: 一.单独写一个脚本 如 auto_scp

  • shell脚本通过expect实现自动单边无密登录功能

    最近项目需要实现单边无密登录,去网上搜索了下相关文章,实践之后发现几乎都不能用.于是结合expect的使用方法自行写了一个,通过验证可用.现在总结在这供需要的朋友参考. 代码如下: EXPECT=/usr/bin/expect $EXPECT <<EOF spawn ssh-keygen -t rsa expect "*ssh/id_rsa*" { send -- "\r" expect "*for no passphrase*" s

  • shell中嵌套执行expect命令实例

    一直都想把expect的操作写到bash脚本里,这样就不用我再写两个脚本来执行了,搞了一下午终于有点小成就,给大家看看吧. 系统:centos 5.x 1.先安装expect 复制代码 代码如下: yum -y install expect 2.脚本内容: 复制代码 代码如下: cat auto_svn.sh #!/bin/bash passwd='123456' /usr/bin/expect <<-EOF set time 30 spawn ssh -p18330 root@192.168

  • 利用expect命令实现Shell自动化交互的方法详解

    背景 linux脚本中有很多场景是进行远程操作的,例如远程登录ssh.远程复制scp.文件传输sftp等.这些命令中都会涉及到安全密码的输入,正常使用命令时是需要人工手动输入密码并接受安全验证的.为了实现自动化远程操作,我们可以借用expect的功能. expect是一个免费的编程工具语言,用来实现自动和交互式任务进行通信,而无需人的干预.expect是不断发展的,随着时间的流逝,其功能越来越强大,已经成为系统管理员的的一个强大助手.expect需要Tcl编程语言的支持,要在系统上运行expec

  • 利用win10自带虚拟机hyper-v安装centos7方法详解

    一.安装win10企业版自带虚拟机 hyper-v 1.控制面板-->程序和功能-->启用或关闭Windows功能 勾上 hyper-v 确定就ok了 2.安装成功后会发现在 左下角"开始"菜单里的"Windows 管理工具" 中出现"hyper-v管理器" 打开hyper-v管理器,首先新建"虚拟交换机" 以便虚拟系统访问网络. 虚拟交换机 创建完成后,接下来我们创建 虚拟机. 这边根据需要选择第一代还是第二代.

  • python解析命令行参数的三种方法详解

    这篇文章主要介绍了python解析命令行参数的三种方法详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 python解析命令行参数主要有三种方法:sys.argv.argparse解析.getopt解析 方法一:sys.argv -- 命令行执行:python test_命令行传参.py 1,2,3 1000 # test_命令行传参.py import sys def para_input(): print(len(sys.argv)) #

  • Python自动化操作Excel方法详解(xlrd,xlwt)

    目录 一.Python操作Excel 7大库对比 二.xlrd 读取excel操作 1. 打开文件 2. 获取所有表名 3. 指定sheet表 4. 对sheet表的行操作 5. 对sheet表的列操作 三.xlwt 写入Excel表操作 1. 写入单个数据 2. 写入多个数据 3. 设置列宽 4. 设置行高 5. 设置单元格风格 一.Python操作Excel 7大库对比 Excel是Windows环境下流行的.强大的电子表格应用.无论是在工作中还是学习中我们都几乎在不间断的使用Excel来

  • Windows下用命令行修改IP地址的方法详解(附批处理文件)

    由于我所处的地方要经常在不同的网络之间切换,比如局域网.系统内部网和外网(光是外网我要常常在3个ADSL网之间切换).我之前一直用的方法是在本机上设置多个不同网段的IP,然后切换路由(Route),这样不同的网段通过不同的网关出去,就可以达到同时访问多个网络的目的.但是这样我发现经常可能出现一些问题,所以我决定用最原始的方法来解决,那就是在要使用某一个网段的时候就只用这个网段的IP,这样就需要不停的更换IP地址.当然,在Windows的"网络连接"属性中这样的更改是很麻烦的,不过还好的

  • 利用Linux防火墙隔离本地欺骗地址的方法详解

    前言 即便是被入侵检测和隔离系统所保护的远程网络,黑客们也在寻找各种精巧的方法入侵.IDS/IPS 不能停止或者减少那些想要接管你的网络控制权的黑客攻击.不恰当的配置允许攻击者绕过所有部署的安全措施. 在这篇文章中,我将会解释安全工程师或者系统管理员该怎样避免这些攻击. 几乎所有的 Linux 发行版都带着一个内建的防火墙来保护运行在 Linux 主机上的进程和应用程序.大多数防火墙都按照 IDS/IPS 解决方案设计,这样的设计的主要目的是检测和避免恶意包获取网络的进入权. Linux 防火墙

  • Python3.6安装卸载、执行命令、执行py文件的方法详解

    开发一个功能,C/C++可能要500行代码,Java可能需要200行代码,然而Python可能需要50行代码,虽然代码少,可能开发的飞起,但是这其实是已牺牲运行效率位代价的,可能C只要1秒,Java需要3秒,最后Python要20秒这种,不过Who Care,对吧,人生苦短,还是来学python吧 下载最新Python安装包 安装Python 如何验证安装成功 在交互模式下执行一句Python命令 在命令行模式下执行一个Python文件 关闭交互模式终端会话窗口 window下如何卸载Pytho

  • Android studio利用gradle打jar包并混淆的方法详解

    本文主要介绍了Android studio利用gradle打jar包并混淆的方法,下面话不多说,来看看详细的介绍吧. 首先打jar包的配置很简单,使用jar的task,可以参考gradle官方文档,具体代码如下: task buildJar(type: Jar, dependsOn: ['assembleRelease']) { destinationDir = file('build/outputs/jar/') appendix = "" baseName = "&quo

  • Python利用Faiss库实现ANN近邻搜索的方法详解

    Embedding的近邻搜索是当前图推荐系统非常重要的一种召回方式,通过item2vec.矩阵分解.双塔DNN等方式都能够产出训练好的user embedding.item embedding,对于embedding的使用非常的灵活: 输入user embedding,近邻搜索item embedding,可以给user推荐感兴趣的items 输入user embedding,近邻搜搜user embedding,可以给user推荐感兴趣的user 输入item embedding,近邻搜索it

  • 利用Redis实现访问次数限流的方法详解

    假设我们要做一个业务需求,这个需求就是限制用户的访问频次.比如1分钟内只能访问20次,10分钟内只能访问200次.因为是用户维度的场景,性能肯定是要首先考虑,那么适合这个场景的非Redis莫属. 最简单的实现,莫过于只是用incr进行计数操作,于是有了下面的代码: long count = redisTemplate.opsForValue().increment("user:1:60"); if (count > maxLimitCount) { throw new Limit

随机推荐