文章目錄
- 一、需求:多臺機器批量遠程執行 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 替我們解決高亮等美觀問題,提升效率