shell脚本实现自动化交互功能
当执行某些命令时,需要你输入内容才可进入下一步,类似这样的场景如果用脚本代码实现,怎么实现呢?下面我们以连接远程服务器,输入密码为例介绍;
sshpass
sshpass -p 123 ssh user@10.xx.xx.xx > /dev/null << EOF
cd /home/user
mkdir abc
EOF
sshpass -p 指定服务器密码;
以上代码实现了连接到远程服务器,并执行对应的操作;但是这个代码运行,会提示“Pseudo-terminal will not be allocated because stdin is not a terminal.”,为了避免这个打印,建议增加-t参数为 ssh 命令启动分配伪终端,再增加-t分配一个强制性的tty终端,加上-tt后,需要加上exit退出终端,优化后的代码如下:
sshpass -p 123 ssh -tt @10.xx.xx.xx > /dev/null << EOF
cd /home/user
mkdir abc
exit
EOF
但是我尝试运行了下,这种写法如果远程命令执行出错,又不会打印错误信息。需要通过echo $?获取执行成功或失败的状态。
echo
echo 123|ssh -tt user@10.xx.xx.xx
echo 123|sudo xxx
以上代码并不能实现密码的输入,因为ssh 和 sudo 并不从管道接收密码,而是直接提示用户输入。
但是echo能用于其它场景,比如echo p|fdisk /dev/sdb 可以支持fdisk命令中的输入;echo Y|apt install xxx 可以支持安装过程中直接输入yes;
# 这种方式可以实现输入密码的功能,-S参数stdin,用于从标准输入读取密码;
echo 123|sudo -S command
expect
好的,那我们进入本篇的核心,就是为了介绍这个expect命令,它可以实现shell不能实现的交互操作,并且可以根据匹配目标内容输入。
方式一
example.exp 文件内容:
#!/usr/bin/expect -f
set ip "[lindex $argv 0 ]"
set content "随便写一段话"
set timeout -1
spawn smbclient //$ip/nas
expect {
"nothing" {send "pp"}
"Password" {send "123456\n";exp_continue}
"smb" { send "cd 研发部/mail\n" }
}
expect "mail" {send_user "\n$content\n"}
send_user "============\n"
# send "exit\n"
# expect eof
interact
- 执行命令 ./example.exp 10.5.23.6 ;
- set {变量名} {变量值} 在expect脚本中定义变量,[lindex $argv 0 ]是入参的第一个参数值;
- set timeout -1 代表expect匹配时永不超时,这里不设置默认10s;
- spawn 用于启动一个进程;
- expect语句有单分支和多分支两种,如果期待的内容必须出现那就走单分支,如果期待的内容可能出现,那就多分支;
- 对于单分支,直接“expect {预期值} {action}”即可,只要能匹配上,且action执行不出错,就会接着执行下一条,无需exp_continue;
- 如果预期值可能会出现,也可能不会出现,就用多分支,它会从上往下匹配,如果未匹配上,就会自动往下执行;如果匹配上了但末尾不定义exp_continue,那就不会接着执行;如果想接着执行,就要定义exp_continue;
- send 是发送执行命令,注意末尾一定要加\n或\r;
- send_user 相当于shell中 的echo,用于打印内容;
- interact 保持远程交互;
- expect eof 交互结束,退回原用户,但还是需要执行退出命令才会退出;
方式二
有些场景,不仅仅需要执行expect交互,前后可能还要执行其它的shell代码,那就需要这样实现。
echo "expect前的shell代码执行"
/usr/bin/expect << EOF
set ip "[lindex $argv 0 ]"
set content "随便写一段话"
set timeout -1
spawn smbclient //192.xx.xx.x/nas
expect {
"nothing" {send "pp"}
"Password" {send "123456\n";exp_continue}
"smb" { send "cd 研发部/mail\n" }
}
expect "mail" {send_user "\n$content\n"}
send_user "============\n"
send "exit\n"
expect eof
EOF
echo "expect后的shell代码执行"
- 执行命令 ./example.exp 10.5.23.6 ;
- 以上代码存在2个问题点,我至今也不明白,欢迎评论区交流~
- 这种实现$ip $content变量值都无法获取;
- 如果末尾写的是 interact ,运行时也会退出远程机器;