文章目录
- 一、需求:多台机器批量远程执行 shell 命令
- 1.1 业务需求拆解为脚本需求
- 1.2 帮助函数:使用说明文档
- 1.3 main 函数框架
- 二、功能:单机 sshp 执行
- 2.1 fullip 函数:实现 ip 补全
- 2.1.1 参数说明
- 2.1.2 定义全局变量
- 2.1.3 实现:正则匹配、if 分支
- 2.1.4 测试
- 2.2 sshp 函数
- 2.2.1 参数说明
- 2.2.2 定义全局变量
- 2.2.3 实现:利用 sshpass 传参
- 2.2.4 测试
- 三、功能:多机批量 batch_sshp 执行
- 3.1 batch_sshp 函数:多机 sshp 执行
- 3.1.1 参数说明
- 3.1.2 实现
- 3.1.2.1 利用 awk 分隔一个参数
- 3.1.2.2 利用 awk 分隔多个参数
- 3.1.2.3 批量执行脚本
- 3.1.3 测试、使用示例
- 3.1.3.1 执行 ls 查看目录
- 3.1.3.2 执行 df 查看磁盘占用率
- 3.1.4 完整脚本
一、需求:多台机器批量远程执行 shell 命令
这个脚本可以执行任何命令,用途是广泛的,例如执行 ls 查看目录,或执行 df 查看磁盘占用率
1.1 业务需求拆解为脚本需求
首先可以写 shell 批量获取各机器的磁盘占用率,通过在各机器执行 df -h | head 实现
希望的使用示例为:
mgr.sh 2.99 2.100 df -h | head
1.2 帮助函数:使用说明文档
首先写 print_help()
# print_helpprint_help() {cat << EOF$0ssh IP1 IP2 ... [cmd] -- ssh 连接 IP,执行 cmdfullip SUFFIX_IP -- 获取完整的 IP 地址EOFexit}
1.3 main 函数框架
然后写 main 函数
# maincase $1 inssh)shiftsshp "$@";;fullip)shiftfullip "$@";;*)print_help;;esac
二、功能:单机 sshp 执行
2.1 fullip 函数:实现 ip 补全
2.1.1 参数说明
然后实现 fullip 函数,先写改函数的参数说明:
# fullip# 根据 ip 后缀,补全完整的 ip 地址# 参数$1 为 ip 地址的后缀fullip() {}
2.1.2 定义全局变量
然后在文件开头,定义全局变量 ip_prefix=192.168
# 全局信息
# ip 前缀
PRE_IP=192.168
2.1.3 实现:正则匹配、if 分支
然后实现 fullip 函数
# 全局信息# ip 前缀PRE_IP=192.168# fullip# 根据 ip 后缀,补全完整的 ip 地址# 参数$1 为 ip 地址的后缀fullip() {[ "x$1" == "x" ] && echo "Error: 请输入 IP 后缀" && exit 10# 已经是完整的 ip 地址if [[ $1 =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; thenecho $1return# 只有 2 位的ip 后缀elif[[ $1 =~ ^[0-9]{1,3}\.[0-9]{1,3}$ ]]; then# 补全完整的 ip 地址echo $PRE_IP.$1returnelseecho -e "Error: 请输入正确的 IP 后缀"exit 101fi}
2.1.4 测试
然后测试脚本, 一切正常:
# 先执行脚本,不输入参数
./mgr.sh
./mgr.shssh IP1 IP2 ... [cmd] -- ssh 连接 IP,执行 cmdfullip SUFFIX_IP -- 获取完整的 IP 地址# 再执行 fullip,输入全量 ip
./mgr.sh fullip 192.168.2.1
192.168.2.1# 再执行 fullip,输入ip 后缀
./mgr.sh fullip 2.99
192.168.2.99
2.2 sshp 函数
然后,写 sshp 函数,通过 sshpass 远程执行命令
2.2.1 参数说明
首先写参数说明
# sshp: 通过 sshpass 执行远程命令
# $1 是补全的 ip
# ssh 协议的用户名和密码,是写死的全局变量
# $@ 是ssh 需执行的 cmd
然后补全函数声明, 其实 shell 函数都是形如 f() {} 的
# sshp: 通过 sshpass 执行远程命令
# $参数 $1 为 ip 地址, 和 shell 的 $1 是一样的,可能是完整的 ip,也可能是 ip 的后缀
# ssh 协议的用户名和密码,是写死的全局变量
# $@ 是ssh 需执行的 cmd
sshp() {}
2.2.2 定义全局变量
然后,定义全局变量,即 ssh 的用户名和密码。
这取决于需求,如果所操作的机器的用户名和密码都是相同的,则可以写死为全局变量。否则可以在脚本参数中输入。
USER=ubuntu
PASSWORD=ubuntu
2.2.3 实现:利用 sshpass 传参
sshp() {sshpass -p $PASSWORD ssh -o StrictHostKeyChecking=no "$USER"@$1 "$@"}
注意,sshpass 是 main 函数的入口,所以其输入的参数 $1 就是整个 shell 的 参数 $1。
而 $1 虽然是 ip,但可能是完整的 ip 或者 仅 ip 后缀。
所以需要在 sshp() 做两步工作:
- 内调用 fullip() 实现 ip 的补全
- 将补全的 ip 传参给 sshpass
因此改写为如下:
# sshp# 利用 sshpass 连接到指定的 ip 地址,并执行命令# 参数 $1 为 ip 地址, 和 shell 的 $1 是一样的,可能是完整的 ip,也可能是 ip 的后#缀sshp() {local remote=$(fullip $1)echo -e "remote is: $remote"# 注意解析完 $1 后,则需要 shift,以便解析后续参数shiftsshpass -p $PASSWORD ssh -o StrictHostKeyChecking=no "$USER"@${remote} "$@"}
2.2.4 测试
# 打印帮助信息
$ ./mgr.sh
./mgr.shssh IP1 IP2 ... [cmd] -- ssh 连接 IP,执行 cmdfullip SUFFIX_IP -- 获取完整的 IP 地址# 远程 ssh 执行一个命令
$ ./mgr.sh ssh 2.99 ls
remote is: 192.168.299
a.txt
b.json
三、功能:多机批量 batch_sshp 执行
3.1 batch_sshp 函数:多机 sshp 执行
需求拆解:
多机 sshp 批量执行,因为要多个机器执行,所以需要填多个 ip,例如以空格分隔
因此 ips 和 cmd 之间要有明确的分隔符,例如 cmd
3.1.1 参数说明
./mgr.sh batch_ssh < ip | ip_suffix …> cmd
# 首先解析 ip 列表
# 其次解析 shell-cmds(通过 cmd 关键字)
bahch_ssh() {}
思路:利用 awk 分隔参数
首先,main 解析 $1,如果是 “batch_ssh”,则执行 batch_ssh() 并将参数 shift
然后,用 awk 按 cmd 关键字,拆分参数为 ips 和 shell-cmd,存储到两个 local 变量里
其次,for ip in ips 遍历得到 ip 列表,每次循环都执行一次 sshp 函数
而,shell-cmd 变量则无需解析,直接在 sshp 函数使用即可
所以,完善的函数声明如下:
# batch_ssh
# 对多个机器,批量执行命令
# 根据 "cmd" 关键字,将命令分成两部分,前一部分是 ips 地址数组,后一部分是命令
# 用 for 遍历 ips 数组,对每个 ip 执行命令
batch_ssh() {}
3.1.2 实现
3.1.2.1 利用 awk 分隔一个参数
# batch_ssh
# 对多个机器,批量执行命令
# 根据 "cmd" 关键字,将命令分成两部分,前一部分是 ips 地址数组,后一部分是命令
# 用 for 遍历 ips 数组,对每个 ip 执行命令batch_ssh() {local ips=$(echo "$@" | awk -F 'cmd' '{print $1}')echo -e "ips: ${ips}"echo -e "ips: ${ips[@]}"}
测试:
./mgr.sh batch_ssh 2.99 2.100
ips: 2.99 2.100
ips: 2.99 2.100
3.1.2.2 利用 awk 分隔多个参数
batch_ssh() {local ips=$(echo "$@" | awk -F 'cmd' '{print $1}')local shellcmds=$(echo "$@" | awk -F 'cmd' '{print $2}')echo -e "ips: ${ips}"echo -e "shellcmds: ${shellcmds}"}
测试:
./mgr.sh batch_ssh 2.99 2.100 cmd ls -lrt
ips: 2.99 2.100
shellcmds: ls -lrt
3.1.2.3 批量执行脚本
batch_ssh() {local ips=$(echo "$@" | awk -F 'cmd' '{print $1}')local shellcmd=$(echo "$@" | awk -F 'cmd' '{print $2}')echo -e "ips: ${ips}"echo -e "shellcmds: ${shellcmd}"for ip in ${ips}; dolocal remote=$(fullip $ip)echo -e "\n-----开始执行: $remote-----"sshp $remote $shellcmddoneecho -e "执行完毕"}
并补全帮助信息
# print_helpprint_help() {cat << EOF$0ssh < IP | IPSUFFIX ...> <shellcmd> -- ssh 连接 IP,执行 cmdbatch_ssh < IP | IPSUFFIX ...> cmd <shellcmd> -- 对多个机器,批量执行命令fullip SUFFIX_IP -- 获取完整的 IP 地址EOFexit}
3.1.3 测试、使用示例
这个脚本可以执行任何命令,用途是广泛的
3.1.3.1 执行 ls 查看目录
./mgr.sh batch_ssh 2.99 2.100 cmd ls -lrt
ips: 2.99 2.100
shellcmds: ls -lrt-----开始执行: 192.168.2.99-----
remote is: 192.168.2.99
a.txt
b.json-----开始执行: 192.168.2.100-----
remote is: 192.168.2.100
c.json
d.ini执行完毕
3.1.3.2 执行 df 查看磁盘占用率
mgr.sh batch_ssh 2.99 2.100 cmd df -h
ips: 2.99 2.100
shellcmds: df -h-----开始执行: 192.168.2.99-----
remote is: 192.168.2.99
Filesystem Size Used Avail Use% Mounted on
udev 16G 0 16G 0% /dev
tmpfs 3.2G 1.8M 3.2G 1% /run
/dev/vda2 49G 25G 22G 54% /-----开始执行: 192.168.2.100-----
remote is: 192.168.2.100
Filesystem Size Used Avail Use% Mounted on
udev 48G 0 48G 0% /dev
tmpfs 9.5G 899M 8.6G 10% /run
/dev/sda1 117G 68G 44G 61% /执行完毕
3.1.4 完整脚本
#!/bin/bash# 全局信息
# ip 前缀
PRE_IP=192.168
USER=ubuntu
PASSWORD=ubuntu# fullip
# 根据 ip 后缀,补全完整的 ip 地址
# 参数$1 为 ip 地址的后缀
fullip() {[ "x$1" == "x" ] && echo "Error: 请输入 IP 后缀" && exit 10# 已经是完整的 ip 地址if [[ $1 =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; thenecho $1return# 只有 2 位的ip 后缀elif[[ $1 =~ ^[0-9]{1,3}\.[0-9]{1,3}$ ]]; then# 补全完整的 ip 地址echo ${PRE_IP}.$1returnelseecho -e "Error: 请输入正确的 IP 后缀"exit 101fi
}# sshp
# 利用 sshpass 连接到指定的 ip 地址,并执行命令
# 参数 $1 为 ip 地址, 和 shell 的 $1 是一样的,可能是完整的 ip,也可能是 ip 的后#缀
sshp() {local remote=$(fullip $1)echo -e "remote is: $remote"shiftsshpass -p $PASSWORD ssh -o StrictHostKeyChecking=no "$USER"@${remote} "$@"
}# batch_ssh
# 对多个机器,批量执行命令
# 根据 "cmd" 关键字,将命令分成两部分,前一部分是 ips 地址数组,后一部分是命令
# 用 for 遍历 ips 数组,对每个 ip 执行命令
batch_ssh() {local ips=$(echo "$@" | awk -F 'cmd' '{print $1}')local shellcmd=$(echo "$@" | awk -F 'cmd' '{print $2}')echo -e "ips: ${ips}"echo -e "shellcmds: ${shellcmd}"for ip in ${ips}; dolocal remote=$(fullip $ip)echo -e "\n-----开始执行: $remote-----"sshp $remote $shellcmddoneecho -e "\n执行完毕"
}# print_help
print_help() {
cat << EOF
$0ssh < IP | IPSUFFIX ...> <shellcmd> -- ssh 连接 IP,执行 cmdbatch_ssh < IP | IPSUFFIX ...> cmd <shellcmd> -- 对多个机器,批量执行命令fullip SUFFIX_IP -- 获取完整的 IP 地址
EOF
exit
}# main
case $1 inssh)shiftsshp "$@";;batch_ssh)shiftbatch_ssh "$@";;fullip)shiftfullip "$@";;*)print_help;;
esac
执行效果
$ ./mgr.sh
./mgr.shssh < IP | IPSUFFIX ...> <shellcmd> -- ssh 连接 IP,执行 cmdbatch_ssh < IP | IPSUFFIX ...> cmd <shellcmd> -- 对多个机器,批量执行命令fullip SUFFIX_IP
环境配置:通常会在 vim 写 shell 脚本,可以用开源的 vimrc 配置 https://github.com/amix/vimrc 替我们解决高亮等美观问题,提升效率