zoukankan      html  css  js  c++  java
  • expect安装和使用

       

      Expect是一个我们常在shell交互时常用到的工具,它主要由expect-send组成。Expect是等待输出内容中的特定字符。然后由send发送特定的相应。Expect的工作流程类似于:小明和小红说:hello,小红发现小明说的是hello,然后就回复小明hi。然后小明说:你好,我是小明。小红发现小明说的是”你好,我是XX“,就回复”你好,我是小红“。

    本文主要内容: 1.安装expect    2 expect的一些基本命令选项   3 expect脚本

    1.  安装expect

      1.1 yum 安装

      yum安装就比较简单了,直接运行yum install expect就可以了. 

    yum  install expect

       1.2 源码安装

      源码安装前我们需要安装unzip和gcc 

    yum  install  unzip gcc -y

       源码安装我们需要下载两个源码包。tcl源码包和expect源码包。 

      下载tcl源码包

    cd /tmp &&wget  http://core.tcl.tk/tcl/zip/release/tcl.zip

       下载expect源码包

    wget https://jaist.dl.sourceforge.net/project/expect/Expect/5.45.3/expect5.45.3.tar.gz

      我们需要先编译安装tcl,因为expect包依赖于tcl。

      解压压缩包并编译安装tcl 

    unzip tcl.zip && cd ./tcl/unix
    ./configure && make && make install

      解压压缩包并编译安装expect.

    cd /tmp && tar -xzvf expect5.45.3.tar.gz && cd expect5.45.3/
    ./configure && make && make install

      检查是否安装好(显示安装好的版本号就是已经安装好了)并创建软链接。

    expect -v
    [root@localhost shell]# expect -v
    expect version 5.45.3
    [root@localhost shell]# ln -s /usr/local/bin/expect  /usr/bin/expect

    2.expect常用的参数选项

      2.1  使用 -c 选项:在命令行运行expect 脚本。

      示例:

    [root@localhost shell]# expect -c 'expect "hi expect" {send "hi shell
    "}'
    hi expect
    hi shell
    [root@localhost shell]# 

      解释下:这个的意思就是当我们输入了"hi expect"后,系统输出"hi shell"表示回应,在"hi shell"后加" "是为了让显示更为直观。

      注意一点就是我们在输入完"hi expect"后要按换行键才会出来"hi shell".

      2.2 使用 -d 选项:显示调试信息。

      示例脚本:

    [root@localhost shell]# cat ssh.exp 
    #!/usr/bin/expect -d
    set timeout 30
    spawn ssh-copy-id 192.168.123.218
    expect {
    "*yes/no" {send "yes
    "; exp_continue}
    "password:" {send "123456
    "}
    }
    expect eof

      当我运行它的时候,显示的信息(主要信息是加粗字体):

    expect version 5.45
    argv[0] = /usr/bin/expect  argv[1] = -d  argv[2] = ./ssh.exp  
    set argc 0
    set argv0 "./ssh.exp"
    set argv ""
    executing commands from command file ./ssh.exp
    spawn ssh-copy-id 192.168.123.218
    parent: waiting for sync byte
    parent: telling child to go ahead
    parent: now unsynchronized from child
    spawn: returns {130244}
    
    expect: does "" (spawn_id exp6) match glob pattern "*yes/no"? no
    "password:"? no
    /usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/root/.ssh/id_rsa.pub"
    
    expect: does "/usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/root/.ssh/id_rsa.pub"
    " (spawn_id exp6) match glob pattern "*yes/no"? no
    "password:"? no
    The authenticity of host '192.168.123.218 (192.168.123.218)' can't be established.
    ECDSA key fingerprint is SHA256:Qh+4R5mpwlU6kK3bf0k53ngm+WpKKnfvL1ZJo+YM3ic.
    ECDSA key fingerprint is MD5:d2:76:6d:33:17:e1:51:83:13:aa:10:ce:f7:1f:9f:32.
    Are you sure you want to continue connecting (yes/no)? 
    expect: does "/usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/root/.ssh/id_rsa.pub"
    The authenticity of host '192.168.123.218 (192.168.123.218)' can't be established.
    ECDSA key fingerprint is SHA256:Qh+4R5mpwlU6kK3bf0k53ngm+WpKKnfvL1ZJo+YM3ic.
    ECDSA key fingerprint is MD5:d2:76:6d:33:17:e1:51:83:13:aa:10:ce:f7:1f:9f:32.
    Are you sure you want to continue connecting (yes/no)? " (spawn_id exp6) match glob pattern "*yes/no"? yes
    expect: set expect_out(0,string) "/usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/root/.ssh/id_rsa.pub"
    The authenticity of host '192.168.123.218 (192.168.123.218)' can't be established.
    ECDSA key fingerprint is SHA256:Qh+4R5mpwlU6kK3bf0k53ngm+WpKKnfvL1ZJo+YM3ic.
    ECDSA key fingerprint is MD5:d2:76:6d:33:17:e1:51:83:13:aa:10:ce:f7:1f:9f:32.
    Are you sure you want to continue connecting (yes/no"
    expect: set expect_out(spawn_id) "exp6"
    expect: set expect_out(buffer) "/usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/root/.ssh/id_rsa.pub"
    The authenticity of host '192.168.123.218 (192.168.123.218)' can't be established.
    ECDSA key fingerprint is SHA256:Qh+4R5mpwlU6kK3bf0k53ngm+WpKKnfvL1ZJo+YM3ic.
    ECDSA key fingerprint is MD5:d2:76:6d:33:17:e1:51:83:13:aa:10:ce:f7:1f:9f:32.
    Are you sure you want to continue connecting (yes/no"
    send: sending "yes
    " to { exp6 }
    expect: continuing expect
    
    expect: does ")? " (spawn_id exp6) match glob pattern "*yes/no"? no
    "password:"? no
    yes
    
    expect: does ")? yes
    " (spawn_id exp6) match glob pattern "*yes/no"? no
    "password:"? no
    /usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
    
    expect: does ")? yes
    /usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
    " (spawn_id exp6) match glob pattern "*yes/no"? no
    "password:"? no
    /usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys
    
    expect: does ")? yes
    /usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
    /usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys
    " (spawn_id exp6) match glob pattern "*yes/no"? no
    "password:"? no
    root@192.168.123.218's password: 
    expect: does ")? yes
    /usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
    /usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys
    root@192.168.123.218's password: " (spawn_id exp6) match glob pattern "*yes/no"? no
    "password:"? yes
    expect: set expect_out(0,string) "password:"
    expect: set expect_out(spawn_id) "exp6"
    expect: set expect_out(buffer) ")? yes
    /usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
    /usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys
    root@192.168.123.218's password:"
    send: sending "123456
    " to { exp6 }
    Number of key(s) added: 1
    Now try logging into the machine, with:   "ssh '192.168.123.218'"
    and check to make sure that only the key(s) you wanted were added.
    expect: read eof
    expect: set expect_out(spawn_id) "exp6"
    expect: set expect_out(buffer) " 
    
    Number of key(s) added: 1
    
    Now try logging into the machine, with:   "ssh '192.168.123.218'"
    and check to make sure that only the key(s) you wanted were added.
    
    "

     还有一些其他参数,但是我觉得对于我用处不大,所以没有在此记录。

    3.expect脚本示例

      简单在这记录下下面会用到的一些命令

      set timeout           #设置超时时间,默认超时时间10s.

      spawn shell command   # spawn后面接的是我们要执行的shell命令

      expect "hi"        #匹配输出内容“hi”

      {send “hello ”}                    # 匹配到内容''hi"后输出“hello”,“ ”是代表是返回字符

      interact             #执行完成后保持交互状态,把控制权交给控制台,这个时候就可以手工操作了。如果没有这一句登录完成后会退出,而不是留在远程终端上。如果你只是登录过去执行一段命令就退出,可改为[expect eof] 

      3.1 简单更改密码脚本

    [root@localhost shell]# cat password.exp
    #!/usr/bin/expect -d                  #"#!/usr/bin/expect"这一行告诉操作系统脚本里的代码使用那一个shell来执行。 -d 启用调试模式(可加可不加)。
    set timeout 30              #设置超时时间为30s spawn
    passwd user5            #spawn是进入expect环境后才可以执行的expect内部命令,如果没有装expect或者直接在默认的SHELL下执行是找不到spawn命令的。所以不要用 “which spawn“之类的命令去找spawn命令。好比windows里的dir就是一个内部命令,这个命令由shell自带,你无法找到一个dir.com 或 dir.exe 的可执行文件。它主要的功能是给ssh运行进程加个壳,用来传递交互指令。 expect "New password:" {send "123456 " } #这个命令的意思是判断上次输出结果里是否包含“New password:”的字符串,如果有则立即返回"123456"," "代表是返回字符,否则就等待一段时间后返回,这里等待时长就是前面设置的30秒 。 expect "new password:" {send "123456 "} #在平常我们设置密码的时候会让我输入一次后再输入一次进行确认,这个是匹配第二次输出,然后再次输入密码。 expect eof                #表示读取到文件结束符

      3.2登陆远程服务器并停留在远程服务器上

    [root@localhost shell]# cat login.exp 
    #!/usr/bin/expect
    spawn ssh 192.168.123.218   #ssh 远程登陆
    expect {
    "*yes/no" {send "yes
    ";exp_continue} #匹配输出内容,返回内容,exp_continue表示继续执行下一步
    "*password" {send "123456
    "}
    }
    interact #执行完成后保持交互状态,把控制权交给控制台,这个时候就可以手工操作了。如果没有这一句登录完成后会退出,而不是留在远程终端上。如果你只是登录过去执行一段命令就退出,可改为[expect eof]

      3.3传输参数执行登陆 

    [root@localhost shell]# cat  login2.exp 
    #!/usr/bin/expect 
    set ip [lindex $argv 0]  #这条命令是将变量ip的值设置为传入进来的第一个参数。[lindex $argv 0]表示的就是第一个参数的值
    set port [lindex $argv 1] #这条命令是将变量port的值设置为传入进来的第二个参数。[lindex $argv 1]表示的就是第二个参数的值
    set passwd "123456"
    spawn ssh $ip -p$port  #使用变量,这里使用的方法跟shell脚本一样
    expect {
        "yes/no" {send "yes
    ";exp_continue}
        "password:" {send "$passwd
    "}
    }
    interact
    [root@localhost shell]# ./login2.exp  192.168.123.218 22 #多个参数直接以空格间隔,第一个参数:192.168.123.218 第二个参数22

       3.4在shell脚本中使用expect

        3.4.1通过添加expect脚本文件(建议这种)   

          实现的功能:输入ip(也就是我们传入的参数),并登陆到这个ip,并保持交互状态(interact

          login.sh脚本内容(shell)

    [root@localhost shell]# cat login.sh 
    #!/bin/bash
    read -p "please input you ip:" -t30 remote_ip  #获取登陆ip内容
    echo "$remote_ip"
    ./login.exp $remote_ip  #运行expect脚本 传入参数

           login.exp脚本内容(expect)

    [root@localhost shell]# cat login.exp 
    #!/usr/bin/expect
    set ip [lindex $argv 0]  #将参数值传递给变量ip
    spawn ssh $ip      #登陆ip
    expect {
    "*yes/no" {send "yes
    ";exp_continue} #匹配登陆信息,返回'yes'
    "*password" {send "123456
    "} #匹配输出信息,返回密码
    }
    interact #登陆成功后保持交互状态,把控制权交给控制台

        3.4.2在shell脚本直接写入expect命令

    #!/bin/bash
    read -p "please input you ip:" -t30 remote_ip  #获取登陆ip内容
    echo "$remote_ip"
    expect -d <<EOF
    spawn ssh $remote_ip
    expect {
    "*yes/no" {send "yes
    ";exp_continue}
    "*password:" {send "Xuansiwei123!
    "}
    }
    exit
    expect eof;
    EOF

        3.4.3 expect匹配不同情况(有密钥文件则退出,没有则添加)  

    sshkey.exp
    
    #!/usr/bin/expect -d 
    spawn ssh-keygen
    expect {
    ".ssh/id_rsa" {send "
    ";exp_continue}
    "Overwrite (y/n)?" exit    #判断是否已经有密钥文件,有则退出,没有就进行添加。
    "Enter passphrase" {send "
    ";exp_continue}
    "Enter same passphrase again:" {send "
    "}
    }
    expect eof

      3.5批量分发公钥到远程主机

      批量分发我主要有三个文件,第一个是ip.txt(用来储存ip和密码的,ip和密码直接以":"间隔,示例:192.168.123.12:123456),第二个文件是add.sh(包含读取ip.txt和批量添加的脚本),第三个文件是 ssh_copy.exp(用来处理交互的脚本)

      ip.txt文件内容

    #ip    password
    192.168.123.110:123456
    192.168.123.111:123@456!

      这里注意下我第二行的ip的密码的最后一位是“!”,它在我shell脚本中的取出密码步骤也给我造成了一点麻烦,直接echo "$i"是报错的,我后面换成了echo "${i}",这样就不会报错了。

      add.sh(shell脚本)

    #!/bin/bash
    IP_file='/root/shell/ip.txt'
    if [ -e  "$IP_file" ]          #检查ip.txt文件是否存在,
    then
        echo -e "33[1;32;40m IP file is exist 33[0m" 
    else 
        echo -e "33[1;31;40m IP file is not exist 33[0m"
        exit 2
    fi
    for i in $(cat ${IP_file})
    do 
        IP=$(echo "${i}" |awk -F":" '{print $1}')  #将ip.txt每行的ip值获取
        PW=$(echo "${i}" |awk -F":" '{print $2}')#将ip.txt每行的ip值获取
      ./ssh_copy.exp $IP $PW 
      if [ $? -eq 0 ] #判断expect脚本是否执行成功
      then

        echo "$IP add is ok "
      else
        echo "$IP add faile"
      fi
    done

      在上面的shell脚本我们还可以再加一个验证是否添加成功了,通过验证是否可以直接ssh登录来判断是否添加成功。

      ssh_copy.exp(expect脚本)

    #!/usr/bin/expect #调试的时候可以加个-d参数,便于查看我们的哪里步骤出现了错误。
    set IP [lindex $argv 0 ]
    set PW [lindex $argv 1 ]
    spawn ssh-copy-id $IP
    expect {
        "yes/no" {send "yes
    ";exp_continue}
        "password:" {send "$PW
    "}
    }
    expect eof

    ssh和ssh-copy-id以及批量多机无密码登陆详解

    这篇文章参考以下大佬的文章。

    https://www.cnblogs.com/lixigang/articles/4849527.html

    https://segmentfault.com/a/1190000002564816

  • 相关阅读:
    FSLIB.NETWORK 简易使用指南
    在CentOS上安装owncloud企业私有云过程
    用于ViEmu的重置为试用状态的Python脚本
    Microsoft.Office.Interop.Excel 报错
    FineUIMvc表格数据库分页,使用CYQ.Data组件
    如何在已有项目中引入FineUIMvc
    按键精灵-常用脚本命令汇集
    微信分享代码
    [教程] 【原创】媒体扫描耗电的彻底解决办法(申精)
    Less学习笔记
  • 原文地址:https://www.cnblogs.com/operationhome/p/9154055.html
Copyright © 2011-2022 走看看