曲径通幽论坛

 找回密码
 立即注册
搜索
查看: 5668|回复: 0
打印 上一主题 下一主题

[系统应用] /lib/functions.sh 中的函数调用简析

[复制链接]

4918

主题

5880

帖子

3万

积分

GROAD

曲径通幽,安觅芳踪。

Rank: 6Rank: 6

积分
34395
跳转到指定楼层
楼主
发表于 2014-12-1 22:44:56 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
/lib/functions.sh 中的函数调用设计的很巧妙,它和 UCI 配置文件以及 eval 命令的相结合,如果不小心,会让你产生迷惑,以致会走些弯路浪费时间,下面简单的谈一谈。
一般情况下,会先执行 . /lib/functions.sh 来加载该文件。在 functions.sh 中,最后一句
  1. [ -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 目录下),于是开始处会有这么一句,比如:
  1. config_load network
复制代码

从上面贴出的代码可以知道,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"                                                                                                        
} 



这里,比较核心的一句是:
  1. 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 配置文件的关系确实很巧妙,如果不注意,会让你比较晕,不明白其中的调用关系。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|曲径通幽 ( 琼ICP备11001422号-1|公安备案:46900502000207 )

GMT+8, 2025-5-3 13:48 , Processed in 0.069186 second(s), 23 queries .

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表