【Linux Shell脚本编程】expect解决脚本交互 + Shell的多进程处理

Tanglu Shell 2018-07-13 14593 0

如果在没有使用密钥认证的情况下,想通过SSH来传输文件给多个主机会面临交互的问题,这在脚本中是非常不友好的。要解决这个问题的话可以使用expect这个工具,它的功能就是提前把交互中需要的内容先写好,然后在脚本执行的时候自动输入。通常用这个工具解决秘钥分发的问题,之后有了秘钥就不需要再使用它了。

1、使用yum安装expect

yum -y expect


2、编写一个使用expect解决ssh交互问题的案例

vi expect.sh
#!/bin/expect
spawn  ssh root@192.168.1.100    #让expect处理该会话,也就是说执行该命令后遇到的交互内容将由expect继续
#下面是提前输入了可能会遇到的交互的内容以及应答方式
expect  {
    "yes/no"  { send "yes\r"; exp_continue }  #遇到引号内的关键词就发送yes指令,
代表回车,后面的exp_continue表示没有遇到的话继续往下执行
    "password" { send "centos\r" };
}
interact #让会话保留在对方那边。因为是ssh连接,所以要保持连接就要将会话停住而不能退出


如果不需要保持交互的话可以写成这样的格式:

#!/bin/expect
spawn  ssh root@192.168.1.100   
expect  {
    "yes/no"  { send "yes\r"; exp_continue }  
    "password" { send "centos\r" };
}
expect "#"    #这里的#其实就是登陆ssh后出现的那个提示符
send "useradd user1\r"
send "echo 123456 | password --stdin user1\r"
expect eof    #结束expect


3、还可以在expect中使用变量,格式如下

#!/bin/expect
set ip 192.168.1.100
set user root

spawn  ssh $user@$ip    #让expect处理该会话,引用了变量

expect  {
    "yes/no"  { send "yes\r"; exp_continue }  
    "password" { send "centos\r" };
}


4、还可以使用位置变量进行传参,括号内是固定格式,不用做变动,0代表第一个参数,以此类推,

#!/bin/expect
set ip [lindex $argv 0]
set user [lindex $argv 1]

spawn  ssh $user@$ip    #让expect处理该会话,引用了变量

expect  {
    "yes/no"  { send "yes\r"; exp_continue }  
    "password" { send "centos\r" };
}


5、最后使用expect执行脚本

expect expect.sh


示例:使用expect批量推送公钥

可以看到该脚本在for循环中用到了{}&这样的组合,这是使用多进程的方式在执行循环,然后使用wait等所有线程都执行完毕后进行最后的finish。使用多进程执行脚本时需要注意的是要结合命名管道(使用mkfifo命令创建命名管道)来控制进程的数量,否则执行大批量操作时会出错

#!/usr/bin/bash
>ip.txt
password=yourpassword

rpm -q expect &>/dev/null
if [ $? -ne 0 ];then 
	yum -y install expect
fi

if [ ! -f ~/.ssh/id_rsa ];then
	ssh-keygen -P "" -f ~/.ssh/id_rsa
fi

for i in {1..254}
do
	{
	ip=192.168.122.$i
	ping -c1 -W1 $ip &>/dev/null
	if [ $? -eq 0 ];then
		echo "$ip" >> ip.txt
		/usr/bin/expect <<-EOF
		set timeout 10
		spawn ssh-copy-id $ip
		expect {
			"yes/no" { send "yes\r"; exp_continue }
			"password:" { send "$password\r" }
		}
		expect eof
		EOF
	fi
	}&
done
wait
echo "finish...."


评论