Shell定义函数以数组作为参数的方法

今天升级syndns.sh脚本,升级为默认DNS 服务器为多个,当一个DNS服务器探测失败后使用下一个DNS服务器继续探测,从而大大提高了脚本的探测能力。在升级过程中,需要用到将默认DNS服务器数组作为参数传递到函数的问题,于是有了本文。

遍历所有参数: $@

1
2
3
4
#!/bin/bash
for para in "$@" ; do
echo $para
done

在上述例子中,使用for循环和$@遍历传递给脚本的所有参数,并逐个输出这些参数。其优点在于简洁,但也存在缺点,即无法指定输出哪一个参数。所以,对于不需要识别参数具体位置的情况,使用变量$@来遍历参数是个不错的方案。

参数的数量:$#

变量$#返回参数的数量,使用它配合$@可以指定输出确定位置的参数。例如

1
2
3
4
5
6
test(){
local paras=("$@")
for ((k = 0; k < $#; k++)); do
echo ${paras[$k]}
done
}

展开数组:${arr[@]}${arr[*]}

for的展开位置,可以使用上述两条命令,来逐一调用数组变量。例如

1
2
3
for ipc in ${SYN_IP[*]}; do
echo "$ipc"
done

数组作为参数传递给函数

需要注意的是,在将数组作为参数传递给函数时,参数形式只能使用"${Arr[*]}"才行。例如

1
SYN_DN2IP "${SYN_GITHUB[*]}" "$SYN_REC"

syndns.sh V2.1

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
#! /bin/sh
#
# Program : syndns.sh
# Version : v2.1
# Date : 2024-12-28 12:10
# Author : fengzhenhua
# Email : fengzhenhua@outlook.com
# CopyRight: Copyright (C) 2022-2025 FengZhenhua(冯振华)
# License : Distributed under terms of the MIT license.
#
# 检测软件依懒, 若未检测到,则自动安装
SYNDNS_DEPEND(){
for VAR in $1 ;do
pacman -Qq $VAR &> /dev/null
if [[ $? != 0 ]]; then
sudo pacman -S $VAR
fi
done
}
# 变量
SYN_EXE="/usr/local/bin/${0%.sh}"
SYN_AUTO="$HOME/.config/autostart/${0%.sh}.desktop"
SYN_SUDO="/etc/sudoers.d/01_$USER"
SYN_HOS="/etc/hosts"
SYN_REC=$(grep "addn-hosts" /etc/dnsmasq.conf |grep "/dev/shm/")
SYN_REC=${SYN_REC#*=}
SYN_ADD="$HOME/.host_dns_autoadd.txt"
SYN_DNSIP=(8.8.8.8 119.29.29.29) # 默认DNS服务器
# Github 网站涉及的所有域名
SYN_GITHUB=(github.githubassets.com central.github.com desktop.githubusercontent.com \
assets-cdn.github.com camo.githubusercontent.com github.map.fastly.net github.global.ssl.fastly.net \
gist.github.com github.io github.com api.github.com raw.githubusercontent.com user-images.githubusercontent.com \
favicons.githubusercontent.com avatars5.githubusercontent.com avatars4.githubusercontent.com \
avatars3.githubusercontent.com avatars2.githubusercontent.com avatars1.githubusercontent.com \
avatars0.githubusercontent.com avatars.githubusercontent.com codeload.github.com \
github-cloud.s3.amazonaws.com github-com.s3.amazonaws.com \
github-production-release-asset-2e65be.s3.amazonaws.com \
github-production-user-asset-6210df.s3.amazonaws.com \
github-production-repository-file-5c1aeb.s3.amazonaws.com githubstatus.com github.community \
media.githubusercontent.com objects.githubusercontent.com raw.github.com copilot-proxy.githubusercontent.com)
#
# 参数1为域名数组,参数2 保存文件, 使用 SYN_DN2IP "${DOMAN[*]}" "outfile"
SYN_DN2IP(){
for hubweb in $1; do
unset SYN_IP
for ((k = 0; k < ${#SYN_DNSIP[@]}; k++)); do
SYN_IP="$(dig @${SYN_DNSIP[$k]} +short $hubweb)"
if [[ ! "$SYN_IP" =~ "#" ]]; then
k=${#SYN_DNSIP[@]}
fi
done
if [[ ! "$SYN_IP" =~ "#" ]]; then
for ipc in ${SYN_IP[*]}; do
echo "$ipc $hubweb" >> $2
done
fi
done
}
# 主程序
SYNDNS_PROCESS(){
cat $SYN_HOS |grep -v '^$'|grep -v '^#'|sort |uniq |sed -r 's/ * / /g' > $SYN_REC
# 需要专门对Github探测
SYN_DN2IP "${SYN_GITHUB[*]}" "$SYN_REC"
if [ -e $SYN_ADD ]; then
cat $SYN_ADD |grep '^[0-9]' |grep -v '^$'|grep -v '^#'|sort |uniq |sed -r 's/ * / /g' >> $SYN_REC
fi
cat $SYN_REC |grep '^[0-9]' |grep -v '^$'|grep -v '^#'|sort |uniq |sed -r 's/ * / /g' > $SYN_REC
echo "$(hostname -i) localhost:" >> $SYN_REC
systemctl is-active --quiet dnsmasq
if [[ $? == 0 ]]; then
sudo systemctl restart dnsmasq.service
else
sudo systemctl start dnsmasq.service
fi
}
# 安装和更新
if [ $# -gt 0 ]; then
if [ $1 == "-i" -o $1 == "-I" ]; then
SYNDNS_DEPEND "dnsutils inetutils dnsmasq jq"
sudo cp -f $0 $SYN_EXE
sudo chmod +x $SYN_EXE
if [ ! -e $SYN_AUTO ]; then
sudo touch $SYN_AUTO
cat > $SYN_AUTO <<EOF
[Desktop Entry]
Name=SynDns
TryExec=syndns
Exec=$SYN_EXE
Type=Application
Categories=GNOME;GTK;System;Utility;TerminalEmulator;
StartupNotify=true
X-Desktop-File-Install-Version=0.22
X-GNOME-Autostart-enabled=true
Hidden=false
NoDisplay=false
EOF
sudo sh -c "cat > /etc/dnsmasq.conf" <<EOA
domain-needed
bogus-priv
resolv-file=/etc/resolv.conf
no-poll
interface=lo
listen-address=127.0.0.1
bind-interfaces
no-hosts
addn-hosts=/dev/shm/dnsrecord.txt
cache-size=9999
port=53
EOA
fi
if [ ! -e $SYN_SUDO ]; then
sudo touch $SYN_SUDO
sudo sh -c "cat > $SYN_SUDO" <<EOB
$USER ALL=(ALL) NOPASSWD: /bin/systemctl restart dnsmasq.service, /bin/systemctl start dnsmasq.service
EOB
fi
elif [[ $1 =~ ".json" || $1 =~ ".dom" ]]; then
if [[ $1 =~ ".json" ]]; then
Address=($(cat $1|jq -r '.children[]' |grep "\"uri\":"))
Address=(${Address[*]#*//})
Address=(${Address[*]/\"uri\":})
Address=(${Address[*]%%/*})
Address=(${Address[*]%%\"*})
Address=(${Address[*]%%*[0-9]}) # 去除非域名
Address=(`echo ${Address[@]}|sed -e 's/ /\n/g'|sort |uniq`)
elif [[ $1 =~ ".dom" ]]; then
Address=($(cat $1))
fi
if [ ! -e $SYN_ADD ]; then
touch $SYN_ADD
fi
SYN_DN2IP "${Address[*]}" "$SYN_ADD"
cat $SYN_ADD |grep '^[0-9]' |grep -v '^$'|grep -v '^#'|sort |uniq -u |sed -r 's/ * / /g' > $SYN_ADD
SYNDNS_PROCESS
fi
fi
# 默认执行主程序
SYNDNS_PROCESS

参考文章