非交互expect

expect: 

          1,在expect环境中,分号;和 [ 有特殊意义,如果要使用其本义,需要转义,如定义参数和变量时,还有分号 ; 和 [ 时需要加反斜杠\转义 

          2,在expect环境中,点号 . 和连字符 - 及逗号 , 和等号 = 不需要转义

          3,在-c 参数中的脚本双引号 "" 需要转义 \" value \"

      实例:在expect环境的免交互式登陆并su到root的脚本,可以自定义传参:

#!/usr/bin/expect  
#set username [lindex $argv 0]  
#set password [lindex $argv 1]  
#set hostname [lindex $argv 2]

set username Gandefeng
set password gandefeng
set passwordroot admin
set hostname [lindex $argv 0]

spawn ssh  $username@$hostname
expect {
         "Connection refused" exit
         "Name or service not know" exit
         "continue connecting" {send "yes\r";exp_continue}
         "password:" {send "$password\r";exp_continue}
         "Last login" {send "su -\r";exp_continue}
         "Password:" {send "$passwordroot\r"}

        }
interact     ##交付交互式环境

 

在bash环境中调用expect

#!/bin/bash

username="Gandefeng"
password="gandefeng"
passwordroot="admin"
host="$1"
expect -c "
spawn ssh $username@$host
expect {
        \"continue connecting\" {send \"yes\r\"; exp_continue}
        \"password:\" {send \"$password\\r\"; exp_continue}
        \"Last login\" {send \"su\ -\r\"; exp_continue}
        \"Password:\" {send \"$passwordroot\r\"}
       }
interact   ##交付交互式环境
       "

或者另一种在bash中调用expect的写法,这种在执行完后回直接退回本地,无法交付交互式环境:(即不要用here document的方式,因为在expect等待interact的时候发现eof已经结束)

#!/bin/bash
username="Gandefeng"
password="gandefeng"
passwordroot="admin"
host="$1"
/usr/bin/expect <<-EOF
spawn ssh $username@$host
expect    "(yes/no)?"
send      "yes\r";exp_continue
expect    "password:" 
send      "$password\r";exp_continue
expect    "Last login" 
send      "su -\r"
expect    "Password:" 
send      "$passwordroot\r"
expect    "*#"
send      "ls -l\r"
EOF

 

 

  

 expect 使用注意事项:

1 使用-c 选项在命令行中执行expect脚本,例如,让expect获得某个输入字符,输出某个字符:(注意转义)

[root@localhost ~]# expect -c "expect  \"5+5\" { send \"10\n\" } "
5+5
10

2 使用-i交互式的执行expect脚本,正常情况下执行expect命令时,如果不带-i 则只会把第一个参数当成脚本的文件名,-i可以把多个参数当成一个连续的表,因而在带有-c的选项的expect脚本时,十分有用,因为expect在执行expect脚本时,是交互执行的。

# expect -i arg1 arg2 arg3  
expect1.1>set argv  
arg1 arg2 arg3  
expect1.2>  

3 用-d选项执行代码的时候,你可以输出诊断的信息

cat sample.exp  
# !/usr/bin/expect -f
expect "\n";send "pressed enter";$ expect -d sample.expexpect version 5.43.0argv[0] = expect  argv[1] = -d  argv[2] = sample.expset argc 0set argv0 "sample.exp"set argv ""executing commands from command file sample.exp  
  
expect: does "" (spawn_id exp0) match glob pattern "\n"? no  
  
expect: does "\n" (spawn_id exp0) match glob pattern "\n"? yes  
expect: set expect_out(0,string"\n"  
expect: set expect_out(spawn_id) "exp0"  
expect: set expect_out(buffer) "\n"  
send: sending "pressed enter" to { exp0 pressed enter}  

4  用-D选项用于启动调试器,它只接受一个布尔值的参数。这个参数表示提示器必须马上启动,还是只是初始化调试器,以后再使用它。

expect -D 1 script 

   -D选项左边的选项会在调试器启动以前被处理。然后,在调试器启动以后,剩下的命令才会被执行。

# expect -c 'set timeout 10' -D 1 -c 'set a 1'  
1: set a 1  
dbg1.0>  

5 通常,expect会在执行脚本之前,把整个脚本都读入到内存中。“-b”选项可以让expect一次只读取脚本中的一行。当你没有写完整个脚本的时候,这是十分有用的,expect可以开始执行这个不完整的脚本,并且,它可以避免把脚本写入到临时文件中。

expect -b  

6 可以使用标识符让expect不解释命令行参数。

cat  print_cmdline_args.exp  
#!/usr/bin/expect  
puts 'argv0 : [lindex $argv 0]';  
puts 'argv1 : [lindex $argv 1]';  

  当执行上面的脚本的时候,会跳过命令行选项,它们会被当成参数(而不是expect选项),如下所示

# expect print_cmdline_args.exp -d -c  
argv0 : -d  
argv1 : -c  

 

 

 

 

其他例子:

#!/usr/bin/expect     
set timeout 30     
spawn ssh -l username 192.168.1.1     
expect "password:"     
send "ispass\r"     
interact    

说明:  
1. [#!/usr/bin/expect]  
    这一行告诉操作系统脚本里的代码使用那一个shell来执行。这里的expect其实和Linux下的bash、windows下的cmd是一类东西。  
注意:这一行需要在脚本的第一行。  
  
2. [set timeout 30]    
    基本上认识英文的都知道这是设置超时时间的,现在你只要记住他的计时单位是:秒  
   
3. [spawn ssh -l username 192.168.1.1]   
    spawn是进入expect环境后才可以执行的expect内部命令,如果没有装expect或者直接在默认的SHELL下执行是找不到spawn命令的。所以不要用 “which spawn“之类的命令去找spawn命令。好比windows里的dir就是一个内部命令,这个命令由shell自带,你无法找到一个dir.com 或 dir.exe 的可执行文件。   
    它主要的功能是给ssh运行进程加个壳,用来传递交互指令。  
  
4. [expect "password:"]  
    这里的expect也是expect的一个内部命令,有点晕吧,expect的shell命令和内部命令是一样的,但不是一个功能,习惯就好了。这个命令的意思是判断上次输出结果里是否包含“password:”的字符串,如果有则立即返回,否则就等待一段时间后返回,这里等待时长就是前面设置的30秒  
  
5. [send "ispass\r"]  
    这里就是执行交互动作,与手工输入密码的动作等效。  
    温馨提示: 命令字符串结尾别忘记加上 “\r”,如果出现异常等待的状态可以核查一下。  
  
6. [interact]   
    执行完成后保持交互状态,把控制权交给控制台,这个时候就可以手工操作了。如果没有这一句登录完成后会退出,而不是留在远程终端上。如果你只是登录过去执行一段命令就退出,可改为[expect eof]  

🙃expect实现ssh无密钥登陆

说明:用了两个脚本,一个bash脚本(send_key.sh),在其中调用另外一个expect脚本(scp_key_to_node.exp),两个脚本放在同一个目录下:

bash脚本:send_key.sh

#!/bin/bash    
ssh-keygen -t dsa    
for (( i = 1; i <= 100 ; i ++ ))    
do    
  ./scp_key_to_node.exp $i    
done    

expect脚本:(scp_key_to_node.exp)

#!/usr/bin/expect    
set timeout 5    
set hostno [lindex $argv 0]    
spawn scp ~/.ssh/id_dsa.pub impala$hostno:~/.ssh/pub_key    
expect "*password*"    
send "111111\r"    
spawn ssh impala$hostno "cat ~/.ssh/pub_key/ >> ~/.ssh/authorized_keys"    
expect "*password*"    
send "111111\r"    
spawn ssh impala$hostno "chmod 600 ~/.ssh/authorized_keys"    
expect "*password*"    
send "111111\r"    
expect eof 

🙃ssh实现自动登录,并停在登录服务器上

#!/usr/bin/expect -f  
 set ip [lindex $argv 0 ]     //接收第一个参数,并设置IP  
 set password [lindex $argv 1 ]   //接收第二个参数,并设置密码  
 set timeout 10                   //设置超时时间  
 spawn ssh root@$ip       //发送ssh请滶  
 expect {                 //返回信息匹配  
 "*yes/no" { send "yes\r"; exp_continue}  //第一次ssh连接会提示yes/no,继续  
 "*password:" { send "$password\r" }      //出现密码提示,发送密码  
 }  
 interact          //交互模式,用户会停留在远程服务器上面.  

🙃根据IP和密码连接到不同的机器.

#!/usr/bin/expect -f  
  
 set ip 192.168.1.130  
 set password admin  
 set timeout 10  
 spawn ssh root@$ip  
 expect {  
 "*yes/no" { send "yes\r"; exp_continue}  
 "*password:" { send "$password\r" }  
 }  

🙃远程登录到服务器,并且执行命令,执行完后并退出

#!/usr/bin/expect -f  
 set ip 192.168.1.130  
 set password admin  
 set timeout 10  
 spawn ssh root@$ip  
 expect {  
 "*yes/no" { send "yes\r"; exp_continue}  
 "*password:" { send "$password\r" }  
 }  
 expect "#*"  
 send "pwd\r"  
 send  "exit\r"  
 expect eof  

🙃远程登录到ftp,并且下载文件

#!/usr/bin/expect -f  
 set ip [lindex $argv 0 ]  
 set dir [lindex $argv 1 ]  
 set file [lindex $argv 2 ]  
 set timeout 10  
 spawn ftp $ip  
 expect "Name*"  
 send "zwh\r"  
 expect "Password:*"  
 send "zwh\r"  
 expect "ftp>*"  
 send "lcd $dir\r"  
 expect {  
 "*file"  { send_user "local $_dir No such file or directory";send "quit\r" }  
 "*now*"  { send "get $dir/$file $dir/$file\r"}  
 }  
 expect {  
 "*Failed" { send_user "remote $file No such file";send "quit\r" }  
 "*OK"     { send_user "$file has been download\r";send "quit\r"}  
 }  
 expect eof  

🙃使用expect调用passwd自动更改密码

#!/bin/bash  
USER=mynameuser  
PASS=oldpassword  
NPASS=newpassword  
expect << EOF  
spawn passwd  
expect "Changing password for ${USER}."  
send "${PASS}\r"  
expect "Enter new UNIX password:"  
send "${NPASS}\r"  
expect "Retype new UNIX password:"  
send "${NPASS}\r"  
expect eof;  
EOF  

🙃完成对服务器的scp任务

#!/usr/bin/expect  
set timeout 10  
set host [lindex $argv 0]  
set username [lindex $argv 1]  
set password [lindex $argv 2]  
set src_file [lindex $argv 3]  
set dest_file [lindex $argv 4]  
spawn scp $src_file $username@$host:$dest_file  
 expect {  
 "(yes/no)?"  
   {  
    send "yes\n"  
    expect "*assword:" { send "$password\n"}  
 }  
 "*assword:"  
{  
 send "$password\n"  
}  
}  
expect "100%"  
expect eof  

说明:

(1)注意代码刚开始的第一行,指定了expect的路径,与shell脚本相同,这一句指定了程序在执行时到哪里去寻找相应的启动程序。代码刚开始还设定了timeout的时间为10秒,如果在执行scp任务时遇到了代码中没有指定的异常,则在等待10秒后该脚本的执行会自动终止。

(2)这个脚本设置了5个需要手动输入的参数,分别为:目标主机的IP、用户名、密码、本地文件路径、目标主机中的文件路径。如果将以上脚本保存为expect_scp文件,则在shell下执行时需要按以下的规范来输入命令:

./expect_scp 192.168.75.130 root 123456 /root/src_file /root/dest_file  

以上的命令执行后,将把本地/root目录下的src_file文件拷贝到用户名为root,密码为123456的主机192.168.75.130中的/root下,同时还将这个源文件重命名为dest_file。

(3)spawn代表在本地终端执行的语句,在该语句开始执行后,expect开始捕获终端的输出信息,然后做出对应的操作。expect代码中的捕获的(yes/no)内容用于完成第一次访问目标主机时保存密钥的操作。有了这一句,scp的任务减少了中断的情况。代码结尾的expect eof与spawn对应,表示捕获终端输出信息的终止。

 

如果需要实现批量scp的任务,则需要再写一个shell脚本来调用这个expect脚本。

#!/bin/sh  
list_file=$1  
src_file=$2  
dest_file=$3  
cat $list_file | while read line  
do  
   host_ip=`echo $line | awk '{print $1}'`  
   username=`echo $line | awk '{print $2}'`  
   password=`echo $line | awk '{print $3}'`  
   echo "$host_ip"  
   ./expect_scp $host_ip $username $password $src_file $dest_file  
done   

指定了3个参数:列表文件的位置、本地源文件路径、远程主机目标文件路径。需要说明的是其中的列表文件指定了远程主机ip、用户名、密码,这些信息需要写成以下的格式:
IP username password

中间用空格或tab键来分隔,多台主机的信息需要写多行内容,如:
192.168.75.130 root 123456
192.168.75.131 knktc testpass

这样就指定了两台远程主机的信息。注意,如果远程主机密码中有“$”、“#”这类特殊字符的话,在编写列表文件时就需要在这些特殊字符前加上转义字符,否则expect在执行时会输入错误的密码。

执行脚本:

./batch_scp.sh ./hosts.list /root/src_file /root/destfile  

 🙃自动化脚本建立主机之间的SSH信任关系

 

#!/usr/bin/ksh  
#usage ./ssh_trust.sh host1 user1 passwd1 host2 user2 passwd2  
#即建立从user1@host1到user2@host2的ssh信任。  
src_host=$1  
src_username=$2  
src_passwd=$3  
  
dst_host=$4  
dst_username=$5  
dst_passwd=$6  
  
#在远程主机1上生成公私钥对  
Keygen()  
{  
expect << EOF  
spawn ssh $src_username@$src_host ssh-keygen -t rsa  
while 1 {  
        expect {  
                 "password:" {  
                              send "$src_passwd\n"  
                               }  
                "yes/no*" {  
                            send "yes\n"  
                          }  
                        "Enter file in which to save the key*" {  
                                        send "\n"  
                        }  
                        "Enter passphrase*" {  
                                        send "\n"  
                        }  
                        "Enter same passphrase again:" {  
                                        send "\n"  
                                        }  
  
                        "Overwrite (y/n)" {  
                                        send "n\n"  
                        }  
                        eof {  
                                   exit  
                        }  
        }  
}  
EOF  
}  
#从远程主机1获取公钥保存到本地  
Get_pub()  
{  
expect << EOF  
spawn scp $src_username@$src_host:~/.ssh/id_rsa.pub /tmp  
expect {  
             "password:" {  
                           send "$src_passwd\n";exp_continue  
                }  
                "yes/no*" {  
                           send "yes\n";exp_continue  
                }     
                eof {  
                                exit  
                }  
}  
EOF  
}  
#将公钥的内容附加到远程主机2的authorized_keys  
Put_pub()  
{  
src_pub="$(cat /tmp/id_rsa.pub)"  
expect << EOF  
spawn ssh $dst_username@$dst_host "mkdir -p ~/.ssh;echo $src_pub >> ~/.ssh/authorized_keys;chmod 600 ~/.ssh/authorized_keys"  
expect {  
            "password:" {  
                        send "$dst_passwd\n";exp_continue  
             }  
            "yes/no*" {  
                        send "yes\n";exp_continue  
             }     
            eof {  
                        exit  
             }   
}  
EOF  
}  
Keygen  
Get_pub  
Put_pub  

 

posted @ 2017-05-21 15:49  Dothraki  阅读(639)  评论(0编辑  收藏  举报