SSH 主机连接管理脚本

使用脚本实现一个 SSH 主机连接管理,解决简单的 ssh 主机连接的管理需求,对于复杂或巨量主机管理需求,不推荐使用本脚本,建议购买安装secureCRT等专业的软件。

脚本命名为smg不带后缀.sh,并放置到PATH中,此命名为命令程序启动命令。例如:你命名为 ssh-m,那么你在命令行中应执行 ssh-m 来启动本脚本。本文使用 smg 来启动脚本。

1
2
3
4
5
6
7
8
9
10
chilisdy@MacBook-Pro Blog % smg
=====================================================================
1 主机: xxx.xxx.xxx.xxx 端口:30044 用户名: root 备注:测试信息
2 主机: 192.168.0.199 端口: 22 用户名: root 备注:测试信息
=====================================================================
请选择连接主机的序号: 2
spawn ssh -p 22 root@192.168.0.199
root@192.168.0.199's password:
Last login: Fri Feb 18 09:18:08 2022 from 192.168.0.76
[root@localhost ~]#

数据的存储放在用户家目录中的.ssh/host_record中,第一次运行脚本会检测并创建该文件。

备份数据:直接备份该文件即可,数据的增删可以直接操作此文件,添加数据注意格式。

有其他需求,自行修改此脚本。

1
2
3
4
5
chilisdy@MacBook-Pro ~ % smg
[检测] .ssh 文件夹不存在,创建...
[检测] host_record 文件不存在,创建...
=====================================================================
=====================================================================

使用smg help 来查看脚本使用帮助,或者直接看脚本源码。

以下为脚本源码:

  • 2022.5.20:增加 scp

    • 因为终端会自动转换~为本机的用户主目录地址,所以远程目标使用~路径时必须使用引号包裹路径
    • 拷贝目录:smg copy 5 -r Downloads "~/"
    • 拷贝文件:smg copy 5 -r Downloads/TablePlus.dmg /root/
  • 2022.2.27:增加直接连接主机方法

  • 2022.2.26:增加免密登录逻辑,记录文件格式对应修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
#!/usr/bin/env bash

function help {
echo "====================================================================="
echo "查看: smg ls"
echo "删除: smg del <序号>"
echo "添加: smg add <格式>, 格式为(空格间隔): IP地址 端口 用户名 密码 登录方式 备注"
echo " - 登录方式: 1 为免密登录 2 为密码登录"
echo "连接: 直接运行 smg 按提示输入序号连接"
echo "直接连接: 运行 smg conn <序号>"
echo "拷贝文件: 运行 smg copy <序号> [-r] 本地目标路径 远程目标绝对路径"
echo "====================================================================="
}

function conn {
local seq=$1
declare -a all_hosts
i=0
IFS=$'\n'

for line in `cat ~/.ssh/host_record`
do
if [[ $line =~ ^#.* ]];then
continue
fi
all_hosts[$i]=$line
let i++
done

if [ ! -n "$seq" ] ;then
exit 0
fi

n1=`echo $seq|sed 's/[0-9]//g'`
if [ ! -z $n1 ]
then
echo "[错误] 请输入正确的数字"
exit 1
fi

if [ $seq -gt $i ]; then
echo "[错误] 输入序号超出已存储主机范围"
exit 1
fi

eval $(echo ${all_hosts[$seq-1]} | awk '{print "ip="$1";port="$2";username="$3";passwd="$4";login_type="$5}')

if [ $login_type -eq 2 ];then
expect -c "
set timeout 30
spawn ssh -p $port $username@$ip
expect {
\"*yes/no*\" {send \"yes\n\";exp_continue}
\"*password*\" {send \"$passwd\n\";}
}
interact
"
elif [ $login_type -eq 1 ]; then
expect -c "
set timeout 30
spawn ssh -p $port $username@$ip
interact
"
else
echo "[错误] 未知的登录方式,请检查主机记录文件: ~/.ssh/host_record,运行 help 指令获取帮助"
fi
}

function connect_host {
declare -a all_hosts
i=0
IFS=$'\n'

for line in `cat ~/.ssh/host_record`
do
if [[ $line =~ ^#.* ]];then
continue
fi
all_hosts[$i]=$line
let i++
done

echo "====================================================================="
j=1
for item in ${all_hosts[*]}
do
echo $j `echo $item|awk '{printf("主机:%15s 端口:%5s 用户名:%7s 备注:%s", $1, $2, $3, $6)}'`
let j++
done
echo "====================================================================="
read -p "[信息] 请选择连接主机的序号: " seq

conn $seq
}

function add {
local ip=$1
local port=$2
local username=$3
local passwd=$4
local login_type=$5
local remark=$6

echo "$ip $port $username $passwd $login_type $remark" >> ~/.ssh/host_record
}

function del {
local num=$(($1 + 1))
sed -i '' `printf '%dd' $num` ~/.ssh/host_record
}

function list {
declare -a all_hosts
i=0
IFS=$'\n'
for line in `cat ~/.ssh/host_record`
do
if [[ $line =~ ^#.* ]];then
continue
fi
all_hosts[$i]=$line
let i++
done
echo "====================================================================="
j=1
for item in ${all_hosts[*]}
do
echo $j `echo $item|awk '{printf("主机:%15s 端口:%5s 用户名:%7s 备注:%s", $1, $2, $3, $6)}'`
let j++
done
echo "====================================================================="
}

function copy {
local seq=$1
local dir_tag=$2
local local_path_file=$3
local remote_path_file=$4
declare -a all_hosts
i=0
IFS=$'\n'

for line in `cat ~/.ssh/host_record`
do
if [[ $line =~ ^#.* ]];then
continue
fi
all_hosts[$i]=$line
let i++
done

if [ ! -n "$seq" ] ;then
exit 0
fi

n1=`echo $seq|sed 's/[0-9]//g'`
if [ ! -z $n1 ]
then
echo "[错误] 请输入正确的数字"
exit 1
fi

if [ $seq -gt $i ]; then
echo "[错误] 输入序号超出已存储主机范围"
exit 1
fi

eval $(echo ${all_hosts[$seq-1]} | awk '{print "ip="$1";port="$2";username="$3";passwd="$4";login_type="$5}')

if [ $login_type -eq 2 ];then
expect -c "
set timeout 30
spawn scp -P $port $dir_tag $local_path_file $username@$ip:$remote_path_file
expect {
\"*yes/no*\" {send \"yes\n\";exp_continue}
\"*password*\" {send \"$passwd\n\";}
}
interact
"
elif [ $login_type -eq 1 ]; then
expect -c "
set timeout 30
spawn scp -P $port $dir_tag $local_path_file $username@$ip:$remote_path_file
interact
"
else
echo "[错误] 未知的连接方式,请检查主机记录文件: ~/.ssh/host_record,运行 help 指令获取帮助"
fi
}



if [ ! -d ~/.ssh ]; then
echo "[检测] .ssh 文件夹不存在,创建..."
mkdir ~/.ssh
fi

if [ ! -f ~/.ssh/host_record ]; then
echo "[检测] host_record 文件不存在,创建..."
touch ~/.ssh/host_record
echo "# IP地址 端口号 用户名 密码 备注" >> ~/.ssh/host_record
fi


if [[ $1 == "add" ]]; then
if [[ $# -ne 7 ]]; then
echo '[错误] 参数不足,按照后面的格式传入(空格间隔): IP地址 端口 用户名 密码 登录方式 备注'
else
add $2 $3 $4 $5 $6 $7
fi
elif [[ $1 == "del" ]]; then
del $2
elif [[ $1 == "ls" ]]; then
list
elif [[ $1 == "help" ]]; then
help
elif [[ $1 == "conn" ]]; then
if [[ $# -ne 2 ]]; then
echo "[错误] 参数不足,需要传入连接的主机序号"
else
conn $2
fi
elif [[ $1 == "copy" ]]; then
if [[ $# -eq 4 ]]; then
copy $2 "" $3 $4
elif [[ $# -eq 5 ]]; then
copy $2 $3 $4 $5
else
echo "[错误] 参数不足,需要传入连接的主机序号,[-r],本地文件路径,远程文件绝对路径"
fi
else
connect_host
fi