/lib/functions.sh 中的函数调用设计的很巧妙,它和 UCI 配置文件以及 eval 命令的相结合,如果不小心,会让你产生迷惑,以致会走些弯路浪费时间,下面简单的谈一谈。
一般情况下,会先执行 . /lib/functions.sh 来加载该文件。在 functions.sh 中,最后一句
- [ -z "$IPKG_INSTROOT" -a -f /lib/config/uci.sh ] && . /lib/config/uci.sh
复制代码
表明它还加载了 uci.sh 文件,因为在 functions.sh 中的一些函数也会用到 uci.sh 中的函数,比如 config_load() :
[Bash shell] 纯文本查看 复制代码 config_load() {
[ -n "$IPKG_INSTROOT" ] && return 0
uci_load "$@"
}
其中 uci_load() 是在 uci.sh 中定义的。
比如你在写某个位于 /etc/init.d 下的启停服务管理脚本时,你一般会先加载 UCI 配置文件(一般都位于 /etc/config 目录下),于是开始处会有这么一句,比如:
从上面贴出的代码可以知道,network 会传递给 uci_load 函数。
现在来看 uci_load 函数,定义如下:
[Bash shell] 纯文本查看 复制代码 uci_load() {
local PACKAGE="$1"
local DATA
local RET
local VAR
_C=0
if [ -z "$CONFIG_APPEND" ]; then
for VAR in $CONFIG_LIST_STATE; do
export ${NO_EXPORT:+-n} CONFIG_${VAR}=
export ${NO_EXPORT:+-n} CONFIG_${VAR}_LENGTH=
done
export ${NO_EXPORT:+-n} CONFIG_LIST_STATE=
export ${NO_EXPORT:+-n} CONFIG_SECTIONS=
export ${NO_EXPORT:+-n} CONFIG_NUM_SECTIONS=0
export ${NO_EXPORT:+-n} CONFIG_SECTION=
fi
DATA="$(/sbin/uci ${UCI_CONFIG_DIR:+-c $UCI_CONFIG_DIR} ${LOAD_STATE:+-P /var/state} -S -n export "$PACKAGE" 2>/dev/null)"
RET="$?"
[ "$RET" != 0 -o -z "$DATA" ] || eval "$DATA"
unset DATA
${CONFIG_SECTION:+config_cb}
return "$RET"
}
这里,比较核心的一句是:
- DATA="$(/sbin/uci ${UCI_CONFIG_DIR:+-c $UCI_CONFIG_DIR} ${LOAD_STATE:+-P /var/state} -S -n export "$PACKAGE" 2>/dev/null)"
复制代码
最后这个 DATA 里边保存有 network 的配置情况,简单的说,它保存了 /etc/config/network 中的内容。
实际上,使用 /sbin/uci 命令,并通过 -P 参数来操作 /var/state 目录(该目录下只有一个 network 文件)和操作 /etc/config 目录是等效的,我以为 /var/state 是一种前向兼容的目录。
重点来了,接着会调用 eval "$DATA" 这条语句。大家知道, eval 命令是会扫描它后面内容中是否包含有需要解析的命令变量,如果有,那么将其解释为命令来执行。回过头来看具体的。
通过 echo $DATA , 可以得到其值:
package network config interface 'loopback' option ifname 'lo' option proto 'static' option ipaddr '127.0.0.1' option netmask '255.0.0.0' option up '1' option device 'lo' config globals 'globals' option ula_prefix 'fd35:3d49:b078::/48' config interface 'lan' option force_link '1' option type 'bridge' option proto 'static' option netmask '255.255.255.0' option ip6assign '60' option ipaddr '192.168.2.1' option up '1' option device 'eth0' option ifname 'br-lan' config interface 'wan' option ifname 'eth1' option _orig_ifname 'eth1' option _orig_bridge 'false' option proto 'pppoe' option username 'beyes_wan' option password 'groad.net' config interface 'wan6' option ifname '@wan' option proto 'dhcpv6' config switch 'cfg073777' option name 'switch0' option reset '1' option enable_vlan '1' config switch_vlan 'cfg091ec7' option device 'switch0' option vlan '1' option ports '0 1 2 3 4'
上面也说过,这些内容就是 /etc/config/network 中的内容,只不过以一种机器易于识别的格式输出来(请参考 uci 中的 export 命令)。
注意里头的内容,包含有 package ,config ,option 这些关键字。同样的,我们也能在 /lib/functions.sh 中找到同样名字的函数。这就是巧妙之所在:
当 eval 解析 $DATA 时,发现包含有 package, config, option 这些关键字,它就会去执行 /lib/functions.sh 中的同名函数。这是因为,eval 会对 $DATA 中的内容进行扫描,然后进行变量的命令替换!要知道,我们调用的 config_load 函数是定义在 funcionts.sh 中的,而在该文件里最后一句也使用了 . /lib/config/uci.sh 加载了 uci.sh 文件,因此在 uci.sh 中用 eval 来解析某个含有和 functions.sh 中定义函数同名的变量时,它会进行相应的替换。
由此可见,functions.sh 中的函数调用和 UCI 配置文件的关系确实很巧妙,如果不注意,会让你比较晕,不明白其中的调用关系。
|