最近做无密码登录主机,需要用到交互脚本,总结脚本交互用法。
1、最简单的交互:echo+管道----前一个命令的输出作为后一个命令的标准输入stdin
比如自动分区:
echo -e "n\n\n\n\n\nw" | fdisk /dev/sdb (等价于有空格的echo -e "n\n p\n 1\n \n \n w" | fdisk /dev/sdb)
2、内联输入重定向:Comand <<EOF EOF(EOF是输入起止符,这个符号可以是任意相同的两个关键字比如end)
比如上面的自动分区可以写成
fdisk /dev/vdb <<EOF
>n
>p
>1
>
>
>w
>EOF
3、有些像ssh这样需要自动输入密码的以上两种方式解决不了,就要用我们今天的主角----expect了
expect是基于TCL语言,专门用来做交互脚本设计的,可以单独写,也可以集成到shell中,基本语法:
#!/usr/bin/expect
set 定义变量
spawn 定义动作命令
expect 启动一个子进程,捕捉spawn执行的结果
send/send_user 跟在expect后面,对于expect捕捉到的结果,发送特定信息给spawn进程,send_user类似与shell中echo,是作为输出提示用的。
当然,expect也可以集成到shell中,集成方式如下:
#!/bin/bash
expect -c "
spawn ssh compute1 lsblk
expect {
"(yes/no)" {send yes\n;exp_continue}
}
expect eof"
#这里注意:expect判断的关键字跟send回复的关键字都是字符串,因为shell里字符串是可以不加引号的,这里前面yes/no可以不用引号,后面yes\n也可以用引号,但是如果用引号要写作“yes\\n”
这里面放一个我写的脚本,作用就是集群所有机器需要做ssh无密码互访,这里发现个很有意思的现象,在expect脚本里面\n居然可以与\r互换,这在shell中是不行的,因为linux中不用\r,只用\n表示回车+换行:
#!/bin/bash
#1、 check if host.txt exsits
if [ ! -e ./hosts.txt ];then
echo "Usage: need host.txt in the same directory,format 'IP HOSTNAME' one line"
else
#修改静态DNS hosts文件
cat ./hosts >>/etc/hosts
#2、生成ssh秘钥
expect -c "
spawn ssh-keygen
expect {
"*exists*" {send n\r} #这里表示秘钥已经存在,会直接跳到expect eof结束
"file" {send \r ; exp_continue} #这里表示不存在,如果需要分几步填充,send信息后必须要加exp_continue,不加会直接跳出expect部分到eof结束,下面的几步不会执行
"passphrase" {send \r ; exp_continue}
"again" {send \r}
}
expect eof"
#3、将公钥传到其他所有主机(grep的关键字需要根据hosts修改!!)
for host in `cat /etc/hosts|grep test|awk '{print $2}'|sort -nr|uniq` #这里发现uniq重行需要重行在一起才能被合并,怪不得都要与sort连用
do
expect -c "
spawn ssh-copy-id -f $host
expect {
\"yes/no\" {send yes\r;expect "*assword" {send 123\r}} #这里前面加不加反斜杠
\"password\" {send 123\r}
}
expect eof"
done
#4、这里本想把上面部分定义到一个函数里直接调用,但发现登录到对方主机后无法执行这里定义的函数,所以将以上2.3两部分添加到copy.sh脚本scp到对方root下,然后登陆过去执行
for host in `cat /etc/hosts|