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

  • 相关阅读:
    POJ 1659 Frogs' Neighborhood
    zoj 2913 Bus Pass(BFS)
    ZOJ 1008 Gnome Tetravex(DFS)
    POJ 1562 Oil Deposits (DFS)
    zoj 2165 Red and Black (DFs)poj 1979
    hdu 3954 Level up
    sgu 249 Matrix
    hdu 4417 Super Mario
    SPOJ (BNUOJ) LCM Sum
    hdu 2665 Kth number 划分树
  • 原文地址:https://www.cnblogs.com/operationhome/p/9154055.html
Copyright © 2011-2022 走看看