使用ssh和scp时常常需要交互式的输入yes和密码,对于自动化运维中这种还需要人工点击完成的情况就比较烦了,linux的expect工具可以优雅的解决此问题。
expect是建立在tcl基础上的一个工具,可以让需要交互的任务自动化的完成,相当于是模拟用户进行交互性操作。
安装
使用方法
定义脚本的执行shell,类似于bash等shell功能。
设置超时时间,单位为秒,设置为-1意为永不超时。
spawn
spawn是进入expect环境后才能执行的命令,不能直接在默认的shell环境中进行执行,主要功能是传递交互指令。
expect
同样是expect内部命令,判断输出结果中是否包含某个字符串,没有即立刻返回,否则就等待一段时间后退出,等待的时间由timeout指定。
send
发送交互值,代替我们手动输入,命令字符串后面加上\r
代表敲回车。
interact
执行完后保持交互状态,把控制权交给控制台。
exp_continue
继续执行接下来的交互操作。
$argv
expect可以接收从bash传递的参数,可以使用[lindex $argv n]
,n从0开始
示例
1 2 3 4 5 6 7 8 9 10 11 [root@slions_pc1 ~]# cat interact # !/usr/bin/expect set timeout 30 set passwd "123" spawn ssh 192.168.100.11 expect { "(yes/no)" { send "yes\r",exp_continue } "password:" { send "$passwd\r" } } expect "#" {send "cat /etc/sysconfig/network-scripts/ifcfg-ens33\r"} interact
输出
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 [root@slions_pc1 ~]# chmod +x interact [root@slions_pc1 ~]# ./interact spawn ssh 192.168.100.11 root@192.168.100.11's password: Last login: Mon Aug 30 19:34:19 2021 from 192.168.100.10 [root@slions_pc2 ~]# cat /etc/sysconfig/network-scripts/ifcfg-ens33 TYPE=Ethernet PROXY_METHOD=none BROWSER_ONLY=no BOOTPROTO=none DEFROUTE=yes IPV4_FAILURE_FATAL=no IPV6INIT=yes IPV6_AUTOCONF=yes IPV6_DEFROUTE=yes IPV6_FAILURE_FATAL=no IPV6_ADDR_GEN_MODE=stable-privacy NAME=ens33 UUID=285fd0e1-1041-4470-abc1-2c97ee6764cf DEVICE=ens33 ONBOOT=yes IPADDR=192.168.100.11 PREFIX=24 GATEWAY=192.168.100.2 DNS1=114.114.114.114 [root@slions_pc2 ~]# exit 登出 Connection to 192.168.100.11 closed. [root@slions_pc1 ~]#
expect使用场景 ssh 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 [root@slions_pc1 ~]# cat expect_ssh # !/usr/bin/expect set timeout -1 set passwd [lindex $argv 0] set port [lindex $argv 1] set dest_ip [lindex $argv 2] set cmd [lindex $argv 3] if {$argc < 4} { #do something send_user "usage: $argv0 <remote_passwd> <remote_port> <remote_addr> <remote_cmd> \n" exit } spawn ssh -p $port $dest_ip $cmd expect { "(yes/no)?" { send "yes\r" expect "password:" { send "$passwd\r" } } "password:" { send "$passwd\r" } } expect eof
验证
1 2 3 4 5 6 7 8 9 10 11 [root@slions_pc1 ~]# chmod +x expect_ssh [root@slions_pc1 ~]# ./expect_ssh 123 22 192.168.100.11 'lsblk' spawn ssh -p 22 192.168.100.11 lsblk root@192.168.100.11's password: NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT sda 8:0 0 20G 0 disk ├─sda1 8:1 0 1G 0 part /boot └─sda2 8:2 0 19G 0 part ├─centos-root 253:0 0 18G 0 lvm / └─centos-swap 253:1 0 1020M 0 lvm [SWAP] sr0 11:0 1 10G 0 rom
改用bash脚本中的写法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 [root@slions_pc1 ~]# cat expect.sh # !/bin/bash password=$1 port=$2 dest_ip=$3 cmd=$4 /usr/bin/expect <<EOF set timeout -1 spawn ssh -p $port $dest_ip $cmd expect { "(yes/no)" { send "yes\r"; exp_continue } "password:" { send "$password\r" } } expect eof EOF
scp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 [root@slions_pc1 ~]# cat expect_scp # !/usr/bin/expect set timeout -1 set passwd [lindex $argv 0] set port [lindex $argv 1] set src_file [lindex $argv 2] set dest_file [lindex $argv 3] if {$argc < 4} { #do something send_user "usage: $argv0 <remote_passwd> <remote_port> <src_file> <dest_file> \n" exit } spawn scp -P $port $src_file $dest_file expect { "(yes/no)?" { send "yes\r" expect "*assword:" { send "$passwd\r"} } "*assword:" { send "$passwd\r" } } expect "100%" expect eof
验证
1 2 3 4 5 [root@slions_pc1 ~]# chmod +x expect_scp [root@slions_pc1 ~]# ./expect_scp 123 22 192.168.100.11:/etc/passwd /root/192.168.100.11.passwd_file spawn scp -P 22 192.168.100.11:/etc/passwd /root/192.168.100.11.passwd_file root@192.168.100.11's password: passwd 100% 1099 706.8KB/s 00:00
ssh免密 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 [root@slions_pc1 ~]# cat ssh.file 192.168.100.10 123 192.168.100.11 123 [root@slions_pc1 ~]# cat sshcopy.sh # !/bin/bash sed -ri '35a\StrictHostKeyChecking no' /etc/ssh/ssh_config systemctl restart sshd if [ ! -f ~/.ssh/id_rsa ] then ssh-keygen -P "" -t rsa -f ~/.ssh/id_rsa fi if [ ! -f ssh.file ] then echo -e "${RED_COL}请确认你的ssh.file已经生成${RESET_COL}" break fi while read line do { USER=`whoami` IP=`echo $line |awk '{print $1}'` PASSWORD=`echo $line |awk '{print $2}'` /usr/bin/expect <<-EOF set timeout -1 spawn ssh-copy-id $USER@$IP expect { "{yes/no}" { send "yes\r"; exp_continue } "password:" { send "$PASSWORD\r" } } expect eof EOF }& done < ssh.file wait