一个shell 开发的小型跳板机,献丑了
笔者的wsl 系统时ubuntu 20
背景
自从微软发布了新一代的 命令行终端工具,windows terminal 加上WSL对linux系统的支持,我就已经对这个工具爱不释手了,WSL可以让我在windows系统主机上,直接操作linux系统,而windows terminal又可以无缝对接WSL,简直完美,最新的WSL2还添加了对docker的支持,更加方便了我平时的工作
其次,xshell 律师函了我们公司,于是公司集体要求卸载xshell,也适用了国产的xshell工具,final shell,功能方面还是挺用心,性能方面,实在是差强人意,就在这时,我接触到了windows terminal, 这款微软开发的良心命令行终端工具,它有很多功能会让你爱不释手收,比如分屏,换主题等等
用它,你的工作页面可以是这样的
当然,如果你足够大胆,有想象力,你的背景可以美到不可方物
我的terminal 打开是直接进入我的本机WSL的,等于登录了一台linux系统主机,我可以随意 登录到任何网络可达的服务器
当前的问题是
而我还是无法完全抛弃xshell,因为当我基于登录一台主机,需要一遍一遍的敲类型ssh -p 9090 test@172.20.0.100
然后在一遍一遍 很厌綦烦的去敲密码登录主机,有人可能会问,堡垒机不香吗,堡垒机的确是香,但不是每台主机都需要添加到堡垒机里
于是我心生了一个想法,开发一个可以直接管理主机登录的脚本,他可以帮我管理我曾经登录过的主机
设计
- 这个脚本要可以记录我曾经登录过的主机,而且,我只要输入一个模糊的关键字,可以帮我检索到那台主机
- 如果搜索不到这台主机,那么就调转录入登录信息,如果登录成功,就想主机信息记录下来,方便下次检索登录
- 最好还要可以跟xshell里一样,可以分组,备注信息,让我知道这台主机是干嘛的
- 如果可以实现方便的拷贝文件,那就最好了,毕竟windows terminal里没有lszrz 这样好用的工具
实现
流程图
脚本帮助文档
这里大概讲一下具体实现
第一次执行脚本,会自动生成两个别名 sshh = ‘bash sshh.sh’
和scpp = ‘bash sshh.sh scp’
录入主机
sshh.sh这个脚本 实现了两种录入主机信息的方式
- 第一种是参数录入
比如您敲入sshh -f nsk@172.20.52.44 -p 3030 -g web -m "nginx服务器"
代表您想要录入的主机信息为
P地址:172.20.52.44
用户名:nsk
ssh端口:3030
存放的主机组:web
备注信息:nginx服务器
这几个参数都有默认值
-f 如果后面只有一个ip地址,那就默认是root用户
-p [默认22端口]
-g [默认default组]
- 第二种是交互式录入
直接输入sshh 52.44
,脚本搜索不到52.44这个字符串,就会直接跳入交互式登录模式,安装指示一步一步输入就可以了,这里添加了一些最基本的验证,如果校验不过,会要求你重复输入哦
登录主机
登录主机,炒鸡简单,直接运行脚本,后面跟上你模糊记得的几个ip地址段就可以了
1. 如果搜索到了一个地址,那就直接登录,如果登录成功,那就让您修改这台主机的记录,知道可以正常登录为止
2. 如果搜索不到,请您直接去录入主机信息
3. 如果搜索到多个,那会全部列出来,供您选择
上传下载文件
你是不是经常为类似的操作 scp -P 3030 nsk@172.20.52.44:/tmp/a.tar .
而头疼,要输入一大堆参数,有时候参数记错了 比如 是-P还是-p? 还要重新输入一遍,费事费力,心力交瘁
sshh.sh 脚本里的scp选择 为你带来了福音,这个选项继承了搜索机制,只要这台需要传文件的主机的信息被sshh.sh成功记录,那么您只要,输入ip的一部分,然后添加指定的目录,类似于这样scpp 38.19:/tmp/a.tar .
脚本会自动帮你补全命令行缺少的部分,直接完成传输,是不是很棒,而且使用系统和scp命令是一致的,因为,脚本就是封装了scp命令而已
scpp 38.19:/tmp/a.tar /tmp #传输172.26.38.19服务器上一个文件至本地
scpp -r /tmp/dir 38.19:/tmp/ #将本地的dir目录传输到目标主机的/tmp目录
操作主机记录
这里的选项一共有四个 info del edit mv 根据字面意思可以理解大概的意思
比如您可以操作
列出主机信息
- 列出当前已有的主机分组
- 列出指定分组的主机
- 列出所有分组主机
代码展示
#!/bin/bash
#主机登录管理脚本
#date 20200603
#author nieshkc
echo_color () { #用法 echo_color faltal “致命信息”
fatal_color="1;5;41m" #红底带闪烁,致命错误
error_color="[1;31m" #红色字体,执行错误信息
warn_color="[1;33m" #黄色字体,警号信息
succ_color="[1;32m" #绿色字体,执行正确信息
info_color="[1;36m" #蓝色自己,提示信息
case $1 in
f*) #fatal
echo -e "\e[${fatal_color}${date_time} $2\e[0m"
;;
e*) #error
echo -e "\e${error_color}${date_time} $2\e[0m"
;;
w*) #warning
echo -e "\e${warn_color}${date_time} $2\e[0m"
;;
s*) #success
echo -e "\e${succ_color}${date_time} $2\e[0m"
;;
i*) #info
echo -e "\e${info_color}${date_time} $2\e[0m"
;;
esac
}
usage(){
echo "说明: <args> 为要搜索的关键字,可以为主机ip中的任意关键字,如果搜索到多个,会悉数列出,如果搜索不到,会调到交互至主机信息录入模式
运行脚本,会自动生成两个别名sshh scpp 别名信息记录在$host_dir/.alias文件中,可手动添加"
echo_color i "添加主机"
echo_color s '\t sshh -f $user@$ip [-p 端口(默认22)] [-g 组名(默认default组)] [-m 备注信息]'
echo -e "\t or"
echo_color s "\t sshh <args>(可以交互式添加主机)"
echo_color i "传文件"
echo_color s "\tbash sshh.sh scp <args>:/tmp ."
echo -e "\t说明: 操作基本和scp一致,可以用scpp别名操作,只需要写出可以识别主机名称的ip地址片段即可,脚本会自动补齐需要的部分"
echo -e "\teg. scpp -r 49.56:/tmp/dir /tmp"
echo_color i "操作主机信息"
echo_color s "\t bash sshh.sh <options> <args>"
echo -e " \t options"
echo -e "\t info 查看当前主机信息"
echo -e "\t del 删除主机信息"
echo -e "\t edit 编辑主机信息"
echo -e "\t mv 移动主机至别的组,目标组若不存在自动创建"
echo_color info "输出主机列表"
echo_color s "\t bash sshh.sh list [options]"
echo -e " \t options"
echo -e "\t" ' "" 查看当前主机组信息'
echo -e "\t" ' all 使用tree列出所有主机'
echo -e "\t" ' $group 列出某个组中的所有主机'
echo "-h|--help 打印帮助信息"
}
check_dir(){
for i in $@
do
[ -d $i ] || mkdir -p $i
done
}
check_status(){
if [[ $? -ne 0 ]] ;then
echo_color e "$1"
exit 1
fi
}
check_command(){
if command -v apt &>/dev/null ;then
tool=apt
else
tool=yum
fi
command -v $1 &> /dev/null
[[ $? -eq 0 ]] || sudo $tool install $1 -y
check_status "$1 安装失败请自行安装"
}
expect_copy_id(){
port=${port:-22}
check_command expect
sed -i '/${full_host##*@}/d' ~/.ssh/known_hosts
rm -f ssh-copy-id_id.*
expect <<EOF
set timeout 5
spawn ssh-copy-id -i ${ssh_key_file}.pub -p ${port} ${full_host}
expect {
"yes/no" {send "yes\n";exp_continue}
"password" {send "${passwd}\n";exp_continue}
"ssh -p" {send "\n"}
"WARNING" {send "\n"}
"Permission denied" {send "\n"}
}
expect eof
EOF
}
save_host_config(){
check_dir $host_dir/$group
cat > $host_dir/$group/${full_host##*@} << EOF
describe="${describe}"
group=${group}
full_host=$full_host
port=$port
passwd=$passwd
create_time=$(date +%Y%m%d-%H:%M)
EOF
}
login_host(){
ssh -o GSSAPIAuthentication=no \
-o HashKnownHosts=no \
-o ConnectTimeout=3\
-o PasswordAuthentication=no \
-o StrictHostKeyChecking=no\
-i ${ssh_key_file} \
${full_host} -p $port
if [[ $? -ne 0 ]];then
# path=$(find ~/.sshh -name "*${host##*@}*") #这个删除逻辑还有问题
# rm -f $path
#get_passwd
expect_copy_id
if [ $? -ne 0 ];then
echo_color info "主机信息不正确,请修改"
sleep 2
vim $path
exit 0
else
login_host
fi
fi
}
search_host(){
str=${1##*@}
scp_code=$2
path=$(find ${host_dir} -type f -name "*$str*")
N=$(find ${host_dir} -type f -name "*$str*"|wc -l) #这个选择逻辑也有问题
if [[ ${N} -eq 1 ]];then #如果可以精确匹配,则用来登录
. ${path}
#专门为scp函数查找主机使用,scp_code是标志
if [[ ${scp_code} == "scp_flag" ]];then
return 0
fi
login_host
exit 0
elif [[ ${N} -eq 0 ]];then
#get_passwd
echo_color warn "未搜到您的主机,请录入主机信息"
read_host_info
else
echo_color info "您有多个选择\n ${path}"
exit 0
fi
}
option_host(){
val=$1
str=${2##*@}
path=$(find ${host_dir} -type f -name "*$str*")
N=$(find ${host_dir} -type f -name "*$str*"|wc -l)
if [[ _$3 != _ ]] ;then
dest_group=${host_dir}/$3
check_dir $dest_group
fi
#根据参数选择动作
if [[ $val == "info" ]];then
action=cat
prompt="您的${path##*/}主机信息是"
elif [[ $val == "del" ]];then
action="rm -f"
prompt="您删除了${path##*/}主机信息"
elif [[ $val == 'edit' ]];then
action="vim"
prompt="您修改了${path##*/}主机信息"
elif [[ $val == 'mv' ]];then
action="mv"
prompt="您将${path##*/}主机移动至 $3 组"
fi
if [[ ${N} -eq 1 ]];then #如果可以精确匹配,则用来登录
echo_color info "$prompt"
$action $path $dest_group
#更新group信息
[[ $? -eq 0 && -n $3 ]] && sed -i "/group=/c\group=$3 " $(find ${host_dir} -type f -name "*$str*")
exit 0
elif [[ ${N} -eq 0 ]];then
#get_passwd
echo_color war "没有您要的主机"
else
echo_color info "您有多个选择\n $path"
fi
}
get_args(){
while getopts 'f:p:g:m:' OPT ;do
case $OPT in
f)
if [[ $OPTARG =~ @ ]];then
arg_user=${OPTARG%%@*}
arg_host_ip=${OPTARG##*@}
else
arg_user=root
arg_host_ip=$OPTARG
fi
;;
p)
echo "arg_arg_port=${OPTARG}"
arg_port=${OPTARG}
;;
g)
echo "arg_group=${OPTARG}"
arg_group=${OPTARG}
;;
m)
echo "arg_describe=${OPTARG}"
arg_describe=${OPTARG}
;;
esac
done
}
get_passwd (){
while true
do
read -s -p "输入主机密码: " passwd
expect_copy_id
if [ $? -eq 0 ];then
save_host_config
break
else
echo_color erro "主机信息输入有误"
fi
done
login_host
}
init_all_login(){
search_host $1 #现搜索是否有记录,如果有,直接登录
read_host_info #交互式输入主机信息,直到可以正确登录为止
}
#交互式读入要登录主机的信息,直到成功登录为止
read_host_info(){
while true;do
while true;do
read -p "输入主机信息'eg. [root@]1.1.1.1 [22]': " _full_host _port
if [[ ${_full_host} =~ @ ]];then
_user=${_full_host%%@*}
_host=${_full_host##*@}
echo $_user $_host
else
_user=root
_host=${_full_host}
fi
#检验ip地址合法性
if [[ $_host =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]];then
full_host=${_user}@${_host}
else
echo_color err "ip地址格式不对,重新输入"
continue
fi
#检验端口合法性
port=${_port:-22}
if [[ ${port} =~ ^[0-9]{1,5}$ && ${port} -le 65535 ]];then
break
else
echo err "输入的端口信息有误"
continue
fi
done
##输入主机属组信息,默认在default组
if [[ -z ${group} ]] ;then
read -p "输入当前主机的组别,默认default组: " _group
group=${_group:-default}
fi
#输入备注信息
if [[ -z ${describe} ]] ;then
read -p "您可以备注下这台主机用途: " _describe
describe=${_describe}
fi
#输入密码
read -s -p "输入密码:" _passwd
passwd=${_passwd}
expect_copy_id
if [ $? -eq 0 ];then
save_host_config
login_host
exit 0
else
continue
fi
done
}
init_constants(){ #全局变量声明
host_dir=~/.sshh
scp_file_dir=~/Desktop/
ssh_key_file=~/.ssh/.id_rsa_sshh
full_host=
port=
passwd=
group=
describe=
}
mk_workdir(){
check_dir ${host_dir}/default ${host_dir}/${group}
}
get_key (){
# sed -i '/${host_ip}/d' known_hosts
if [ ! -f ${ssh_key_file} ];then
echo "开始生成密钥对文件"
check_dir ~/.ssh
ssh-keygen -q -P "" -t rsa -f ${ssh_key_file}
cat ~/.ssh/${ssh_key_file}.pub >>/root/.ssh/authorized_keys
fi
}
init_args(){
host_ip=${arg_host_ip}
port=${arg_port:-22}
user=${arg_user:-root}
full_host="${user}@${host_ip}"
group=${arg_group:-default}
describe=${arg_describe}
}
mk_alias(){
file_abs_path=$(readlink -f "$0")
cat >$host_dir/.alise <<EOF
#ssh-manager generator
alias sshh="bash ${file_abs_path}"
alias scpp="bash ${file_abs_path} scp"
EOF
SHELL=$(grep $(whoami) /etc/passwd)
rc_file=~/.${SHELL##*/}rc
if ! $(grep "ssh-manager" ${rc_file} &>/dev/null);then
cat $host_dir/.alise >> ${rc_file}
fi
}
scp_file(){
local host_str url_path dest_path scp_command
#传目录
if [[ $@ =~ '-r' ]];then
if [[ $3 =~ ":" ]];then
##从服务器传至当前主机
search_host ${3%%:*} scp_flag
url_path=$full_host:${3##*:}
dest_path=${4:-${scp_file_dir}}
a=$(scp -B -r -P ${port} ${url_path} ${dest_path} 2>&1)
if [[ $a =~ "Host key verification failed" ]];then
echo_color warn "${full_host##*@}这台主机还未加入管理,请录入信息"
read_host_info
else
echo_color erro "$a"
fi
else
##从当前主机传至服务器
search_host ${4%%:*} scp_flag
dest_path=$full_host:${4##*:}
url_path=${3}
a=$(scp -B -r -P ${port} ${url_path} ${dest_path} 2>&1)
if [[ $a =~ "Host key verification failed" ]];then
echo_color warn "${full_host##*@}这台主机还未加入管理,请录入信息"
read_host_info
else
echo_color erro "$a"
fi
fi
else
#传文件
if [[ $2 =~ ":" ]];then
##从服务器传至当前主机
search_host ${2%%:*} scp_flag
url_path=$full_host:${2##*:}
dest_path=${3:-${scp_file_dir}}
scp -B -P ${port} ${url_path} ${dest_path}
if [[ $a =~ "Host key verification failed" ]];then
echo_color warn "${full_host##*@}这台主机还未加入管理,请录入信息"
read_host_info
else
echo_color erro "$a"
fi
else
##从当前主机传至服务器
search_host ${3%%:*} scp_flag
dest_path=$full_host:${3##*:}
url_path=${2}
a=$(scp -B -P ${port} ${url_path} ${dest_path} 2>&1)
if [[ $a =~ "Host key verification failed" ]];then
echo_color warn "${full_host##*@}这台主机还未加入管理,请录入信息"
read_host_info
else
echo_color erro "$a"
fi
fi
##从当前主机上传至服务器
fi
}
list_dir(){
check_command tree
if [ -z $1 ] ;then
tree -L 1 ${host_dir}
elif [ $1 == "all" ];then
tree ${host_dir}
else
tree ${host_dir}/$1
fi
}
main () {
init_constants
mk_workdir #创建工作目录
get_key #生成主机密钥
mk_alias #生产别名文件
case $* in
-f*) #走命令行 传参方式登录
get_args $* #搜集命令行参数
init_args #初始化常量函数
get_passwd
;;
""|-h|--help)
usage
;;
info*)
option_host info $2
;;
del*)
option_host del $2
;;
edit*)
option_host edit $2
;;
mv*) #更换主机的组别,如果目标组存在,就移动过去,没有就创建
option_host mv $2 $3
;;
scp*)
scp_file $@
;;
list*)
list_dir $2
;;
*)
init_all_login $1
;;
esac
}
main $*
欢迎大家批评指正