纯手撕shell下远程连接工具,解决shell远程窗口无法自适应问题
一,前言
- mac 下没有找到一款可以和windows 下的xshell相当的远程连接工具
- 在网上找了很多expect的脚本,代码都写的好烂,而且都没有解决窗口自适应的问题
- 鉴于此,于是自己手写了一个远程连接工具
二,准备工作
shell 下的编程知识
- 函数编程
- 流程控制
- 字符串截取
- sed命令
- grep 命令
- expect 编程
其中最复杂的就是expect编程
命令 | 作用 | 说明 |
---|---|---|
send | 发送信息 | 向进程发送指定的信息 |
expect | 接收信息 | 命令和send相反,捕获进程返回的信息 |
spawn | 启动新进程 | spwan 后的 send与expect 都是和spawn 最新打开的进程进行交互 |
interact | 允许用户交互 | 停留在登录的服务器中,不退出来 |
三,案例分享,shell脚本中嵌套expect脚本
效果入下图:
源代码
- 我使用了两个文件来实现的,一个是服务器的配置文件,一个是shell脚本文件;配置文件主要用来存放服务器的登录信息,shell脚本文件主要用来执行登录命令
🔥 ls
config.txt sl.sh
sl.sh 文件内容如下:
#!/bin/bash
configPath="./config.txt"
# 一级目录
firstMenu=()
# 二级目录
secondMenu=''
# 一级目录菜单行数下标集合
firstMenuLines=()
# 设置一级菜单栏值与下标
setFirstMenu(){
# 设置一级菜单栏值数组
menusTmp=$(cat $configPath|grep '>>>>'|sed -n 's/>>>>//pg')
firstMenu=(${menusTmp})
# 设置一级菜单栏下标数组
for tmpMenu in `cat -n $configPath|grep '>>>>'`
do
if [[ ! $tmpMenu =~ '>>>>' ]]
then
# 往数组中添加下标
firstMenuLines[${#firstMenuLines[@]}]=$tmpMenu
fi
done
}
setFirstMenu
# 显示一级菜单栏
showFirstMenu(){
# 显示菜单栏
echo -e "\033[36m########################### select group ############################\n"
for(( m=0; m<${#firstMenu[@]}; m++ ))
do
echo -e "\033[32m ${m} ************************ ${firstMenu[m]}"
done
echo -e "\n"
}
showFirstMenu
# 设置二级菜单
setSecondMenu(){
read -p "Enter your num of options: " menuIndex
# 截取开始位置
firstIndex=${firstMenuLines[$menuIndex]}
# 截取结束位置
# 条件:是否为数组firstMenuLines最后一个元素,是,则取文件最后一行,否:则取下一个下标
endMenuLinesIndex=$(( ${#firstMenuLines[@]} - 1 ))
if [ $menuIndex -eq $endMenuLinesIndex ]
then
endIndex=$( cat $configPath | wc -l )
else
endIndex=${firstMenuLines[$menuIndex+1]}
fi
# 显示范围
showScope=$(( $endIndex - $firstIndex ))
nodeTmp=$( cat $configPath | grep -A $showScope ${firstMenu[$menuIndex]}|grep 'ip'|sed -n 's/ip=//pg' )
secondMenu=($nodeTmp)
}
setSecondMenu
# 显示二级菜单
showSecondMenu(){
clear
echo -e "\033[36m########################### select services ############################\n"
for(( n=0; n<${#secondMenu[@]}; n++ ))
do
echo -e "\033[32m ${n} ************************ ${secondMenu[n]}"
done
echo -e "\n"
}
showSecondMenu
# 获取用户登录信息
getUserInfo(){
read -p "Enter your num of options: " nodeIndex
ip=${secondMenu[$nodeIndex]}
userInfoTmp=$( cat $configPath | grep -A 2 $ip )
userInfo=($userInfoTmp)
for info in ${userInfo[@]}
do
if [[ $info =~ "ip=" ]]
then
ip=${info#ip=}
elif [[ $info =~ "name=" ]]
then
name=${info#name=}
else
passwd=${info#passwd=}
fi
done
}
getUserInfo
# 登录服务器
login(){
expect -c "
trap {
set rows [stty rows]
set cols [stty columns]
stty rows \$rows columns \$cols < \$spawn_out(slave,name)
} WINCH
set timeout 5;
spawn ssh -o TCPKeepAlive=yes -o ServerAliveInterval=4 ${name}@${ip} ;
expect {
\"*assword\" {send \"${passwd}\r\" }
\"yes/no\" {send \"yes\r\"; exp_continue }
\"yinguohai':\" {send \"${passwd}\r\" }
} ;
interact;
"
}
login
注意点一:
stty rows \$rows columns \$cols < \$spawn_out(slave,name)
stty 中的 $rows , $cols , $spawn_out 前面都需要加 转移符 \ 。 因为 trap~WINCH中的内容是需要expect来解析的,$rows , $cols, $spawn_out 不能在bash中进行解析,否则都是空,无法传入到expect 中去执行了。
2.config.txt 文件内容
>>>>test
[node1]
ip=47.52.00.11
name=root
passwd=123
>>>>zyc
[node1]
ip=47.70.89.12
name=root
passwd=123456
[node2]
ip=47.222.78.109
name=root
passwd=1234
>>>>lc
[node1]
ip=47.95.11.49
name=root
passwd=Y123455