shellshell实现交互

博观而约取,厚积而薄发。这篇文章主要讲述shellshell实现交互相关的知识,希望能为你提供帮助。

目录
??一、SHELL??
??1.1 输入单个指令??
??1.2 输入多行指令??
【shellshell实现交互】??限制输入内容的个数??
??控制输入内容的可见性??
?? 二、Expect??
??简介??
??实例??
??SSH登录??
?? FTP文件同步??



一、SHELL1.1 输入单个指令自动输入yes
echo "y" | yum install wget ,等同于yum -yinstall wget
自动输入回车
echo -e "\\n" | yum remove wget
**`echo -e` 的小知识**
若字符串中出现以下字符,则特别加以处理,而不会将它当成一般文字输出:
??\\a?? 发出警告声;
??\\b?? 删除前一个字符;
??\\c?? 最后不加上换行符号;
??\\f?? 换行但光标仍旧停留在原来的位置;
??\\v?? 与\\f 相同;
??\\n?? 换行且光标移至行首;
??\\r?? 光标移至行首,但不换行;
??\\t?? 插入 ??tab?? 符号;
??\\\\?? 插入 ‘\' 字符;
??\\nnn?? 插入 nnn(八进制)所代表的 ASCII 字符;
1.2 输入多行指令输入多行指令我们需要借助输入重定向操作符 ??< < ??
以下面这个脚本为例
multi.sh

#!/bin/bash

read -p "enter number:" no
read -p "enter name:" name
echo "you have entered $no, $name"


借助 ??< < ?? 符号进行自动化输入

#!/bin/bash
sh multi.sh < < EOF
1
mutoe
EOF


但是有时候这种方法并不生效,比如 ??ssh-keygen?? 命令,那只有借助强大的 ??expect?? 命令了

read -p "you are sure you wang to xxxxxx?[y/n]" input
echo $input
if [ $input = "y" ]; then
echo "ok "
fi


限制输入内容的个数我们还可以使用 read命令的-n 选项,此选项后面需要接一个数字,可以限制输入内容的个数。

#!/bin/bash
read -p "May I ask your name: " name
echo "Hello $name"
read -n1 -p "Press any key to exit"
echo
exit 0


控制输入内容的可见性目前,我们输入的内容都是可见的,但有些敏感的数据,如密码,信用卡号等信息,输入时并不想可见。那么可以使用??read -s??。
shellshell实现交互

文章图片

这时再输入时,就有一个钥匙的标识,而且输入时不可见。

二、Expect简介 expect是一个免费的编程工具,用来实现自动和交互式任务通信,安装 ??yum install -y expect??
Expect的语法:
关键命令send、expect、spawn和interact:
  • send:用于向进程发送字符串,注意一定要在末尾加\\r回车
  • expect:从进程接收字符串
  • spawn:启动新的进程
  • interact:允许用户继续交互
expect 有两种用法,一种是直接写 expect 解释器的脚本,和 bash 类似,以 ??#!/usr/bin/expect?? 开头
下面是一个合格的 expect 脚本示例

#!/bin/expect

set IP[lindex $argv 0] # 读取第1个参数设置为 IP 变量
set PASSWD [lindex $argv 1] # 读取第2个参数设置为 PASSWD 变量
set CMD[lindex $argv 2] # 读取第3个参数设置为 CMD 变量

spawn ssh $IP $CMD # spawn 来给命令加壳,以便于断言输出
expect { # expect 是断言命令
# 如果读取到屏幕上输出 (yes/no) 信息,则输入 "yes" 并按下回车键
# exp_continue 是继续等待花括号内的断言, 如果不加这一句会直接跳出 expect
"(yes/no)?" { send "yes\\r"; exp_continue }

"password:" { send "$PASSWD\\r" } # 如果读取到屏幕上输出 password 信息,则输入 PASSWD 变量中的内容
"*host " { exit 1 } # 如果读取到 "No route to host" 等内容, 就以非0状态退出
}
expect eof # 等待命令执行结束


需要注意的是,在 expect 解释器内, 除了几个特定关键字的命令,其他命令都不可用,这种方式适用于执行命令较少,单次需要交互较多的自动化脚本
第二种用法是在 bash 脚本中执行 expect 配合重定向操作符, 在有大量脚本需要执行的情况下推荐使用该方式
下面是我在 ??certbot?? 命令时使用的 shell 脚本,以供参考

#!/bin/bash

sudo expect < < EOF
spawn certbot --nginx
expect {
"Enter email address" { send "mutoe@foxmail.com\\n"; exp_continue}
"Please read the Terms of Service" {send "A\\n"; exp_continue}
"Would you be willing to share your email address" {send "N\\n"; exp_continue}
"Which names would you like to activate HTTPS for" {send "\\n"; exp_continue}
"You have an existing certificate that has exactly the same domains" {send "1\\n"; exp_continue}
"Please choose whether or not to redirect HTTP traffic to HTTPS" {send "2\\n"; exp_continue}
eof
}


expect是关键的部分,在英文中,expect有“期待”的意思,采用了tcl的模式-动作语法,此语法有以下几种模式:
单一分支语法:

expect "hello" {send "you said hello"}


多分支模式语法:

expect {
"hello" {send "hello\\r"; exp_continue}
"world" {send "world\\r"; exp_continue}
"how are you ?" {send "Fine,thanks\\r"}
}


实例SSH登录
例:expect脚本ssh.exp内容:

#!/usr/bin/expect
set timeout 2
spawn ssh wan@10.229.130.107
expect {
"[Pp]assword" {send "123456\\r"; }
"[Yy]es/no" {send "yes\\r"; exp_continue}
}
...
...
...
send "exit\\r"
exit 0

#send:用于向进程发送字符串,注意一定要在末尾加\\r回车
#expect:从进程接收字符串
#spawn:启动新的进程
#interact:允许用户继续交互


FTP文件同步
经常要面临在某个环境编辑代码然后需要自动同步更新到其他机器的情况。对于这种问题,我们也可以借助expect脚本来实现。
例:
配置文件为sync.cfg:

#","分隔的内容分别为需要同步的文件、目标路径和目标主机密码
/home/long/tmp/*.py,wan@10.229.130.107:/home/wan/tmp,123456


shell脚本sync_ftp.sh内容:

#!/bin/bash
#读取配置信息
for line in `cat sync.cfg`
do
#删除存在的信息文件,避免数据污染
if [ -f info.dat ]
then
rm -rf info.dat
fi
touch info.dat

#获取路径和密码
srcaddr=`echo $line | cut -d , -f 1`
destaddr=`echo $line | cut -d , -f 2`
passwd=`echo $line | cut -d , -f 3`

#查找指定日期需要同步的文件
for tmp_file in `find $srcaddr -mtime 1 -type f`
do
echo $tmp_file > > info.dat
done

#调用expect脚本进行同步
expect sync_ftp.exp $destaddr $passwd

#判断是否同步成功
if [ $? -eq 0 ]
then
rm -rf info.dat
echo "all success!"
else
echo "sync $destaddr fail!"
fi
done

exit 0


expect脚本sync_ftp.exp:

#!/usr/bin/expect
set timeout 20

#读取输入参数
set remotehost [lindex $argv 0]
set remotepass [lindex $argv 1]

#启动新进程,运行sftp协议
spawn sftp $remotehost
expect "[Pp]assword*"
send "$remotepass\\r"

#发送文件
expect "sftp> "
set content [ open info.dat ]
while{ [ gets $content local_file ] != -1 }
{
send "put $local_file\\r"
expect "sftp> "
}
send "exit\\r"
exit 0


由于文件同步是每天都需要进行的,因此可以结合定时任务crond来进一步简化工作。crond是类unix系统下用来周期执行某种任务或者事件的一个守护进程,配置信息存放在/etct/crontab文件中。crontab是系统服务crond的控制命令。关于crontab的使用,在进程章节已经进行了介绍,这里就不再赘述了。
Expect命令是很强大的,可以实现各种自动化操作。自此,你不仅可以写出各种厉害的shell脚本,还能让它们自动执行,对于日常工作的处理将会有大大的提升。
注:expect命令
[set timeout 30]
基本上认识英文的都知道这是设置超时时间的,现在你只要记住他的计时单位是:秒

[spawn ssh -l username 192.168.1.1]
spawn command命令会fork一个子进程去执行command命令,然后在此子进程中执行后面的命令;
spawn是进入expect环境后才可以执行的expect内部命令,如果没有装expect或者直接在默认的SHELL下执行是找不到spawn命令的。所以不要用 “which spawn“之类的命令去找spawn命令。好比windows里的dir就是一个内部命令,这个命令由shell自带,你无法找到一个dir.com 或 dir.exe 的可执行文件。
它主要的功能是给ssh运行进程加个壳,用来传递交互指令。

[expect "password:"]
这里的expect也是expect的一个内部命令,有点晕吧,expect的shell命令和内部命令是一样的,但不是一个功能,习惯就好了。这个命令的意思是判断上次输出结果里是否包含“password:”的字符串,如果有则立即返回,否则就等待一段时间后返回,这里等待时长就是前面设置的30秒

[send "ispass\\r"]
这里就是执行交互动作,与手工输入密码的动作等效。
温馨提示: 命令字符串结尾别忘记加上 “\\r”,如果出现异常等待的状态可以核查一下。

[interact]
执行完成后保持交互状态,把控制权交给控制台,这个时候就可以手工操作了。如果没有这一句登录完成后会退出,而不是留在远程终端上。如果你只是登录过去执行一段命令就退出,可改为[expect eof]
expect可以让你使用“-c”选项,直接在命令行中执行它,例如:
expect -c \'
spawn git pull
expect {
"Usernam*" {send "code_manager@ren001.com\\n"; exp_continue }
"Passwor*" {send "$GIT_CODE_PASSWORD\\n" ; exp_continue}
}
\'



    推荐阅读