1.首先安装tcl和expect(先安装tcl,再安装expect,自行百度),sshpass
2.我想把本地文件同步到其他几台机器上,因此,需要知道机器的ip,username,password,我用一个文件来存放这些内容machine_info,内容如下:
127.0.0.1 username password
192.168.12.12 usename password
编写expect shell
#!/usr/bin/expect -f
set timeout -1 #永不超时,默认为10s
set src_file [lindex $argv 0] #argv参数读取,必须是argv
set user_name [lindex $argv 1]
set host_ip [lindex $argv 2]
set dest_file [lindex $argv 3]
set password [lindex $argv 4]
spawn scp $src_file $user_name@$host_ip:$dest_file
expect {
"*(yes/no)?"
{
send "yes\r"
expect "*assword:" {
send "$password\r"
}
}
"*assword:"{
send "$password\r"
}
}
expect eof
编写批量 shell,传的参数为文件名和上传到机器的路径
#!/bin/bash
#$0含义
file_name=$1 # =不能空格
file_path=$2
#逐行读取machine_info里面的数据,while read不能改
while read line
do
ip=`echo $line | cut -d' ' -f 1`
user_name=`echo $line | cut -d' ' -f 2`
password=`echo $line | cut -d' ' -f 3`
sshpass -p $password ssh $user_name@$ip mkdir -p $file_path </dev/null #后面的</dev/null很重要,还有记住这种写法
./expect.sh $file_name $user_name $ip $file_path $password
done < machine_info
注:mkdir没有为你创建,有的话就什么都不做,为什要加上/dev/null?
原因是while重定向机制,machine_info文件中的信息都已经读入并重定向给了整个while语句。所以当我们在while循环中再一次调用read语句,就会读取到下一条记录。问题就出在这里,ssh语句正好会读取输入中的所有东西,可以用下面命令证明:
sshpass -p $password ssh $user_name@$ip cat
会发现打印出所有内容,这就导致调用完ssh语句后,输入缓存中已经都被读完了,当read语句再读的时候当然也就读不到纪录,循环也就退出了。因此就要让ssh重定向,就要加/dev/null,或者改用for来执行循环,还有的说ssh加一个-n也行。