Linux下头文件重复定义,Shell脚本避免多次重复source

source语法的引入,使得shell的脚本也可以像其它语言一样,一份代码能够分成多个模块,基本的模块可以像库文件一样被多个脚本使用。例如/etc/init.d/functions,它被多个服务脚本使用。

source除了导入库代码的作用之外,它还可以用于导入配置文件,这个在Linux系统中使用的非常广泛,因为大多数配置文件都是文本格式的,而shell本身又没有解析过于复杂的配置文件的能力,例如xml、json等。典型的例子有,/etc/default/rcS或者/etc/sysconfig/network等等。

也正因为如此,一旦Shell脚本的代码量到达一定的规模,模块化的趋势是必然的,很多地方都会用到source,所以理解source是很有必要的。source的过程,其实就是将脚本导入到当前的执行环境下,并且依次执行其中的代码。因此对于被source的脚本来说,它的一切环境变量都是与当前环境保持一致的,被source脚本中对环境做的任何改动都会影响到当前的执行环境。

下面的例子很好的说明了这点,假设有两个脚本a.sh和b.sh,它们的内容如下所示:

[kodango@devops workspace]$ cat a.sh

echo "Before source: $(pwd)"

. ./b.sh

echo "After source: $(pwd)"

[kodango@devops workspace]$ cat b.sh

cd /tmp

现在执行下a.sh,会获得以下的结果:

[kodango@devops workspace]$ sh a.sh

Before source: /home/kodango/workspace

After source: /tmp

可见,b.sh中对当前路径的变化影响到了a.sh脚本。

那如果多次source同一个脚本会怎么样?答案是,被source的脚本的代码会被执行多次。如果是函数的定义还好,多次执行不会有什么影响,但是如果是变量的定义就会有问题了,同样用一例子来说明一下这个场景。

假设a.sh和b.sh两个脚本的内容如下所示:

[kodango@devops workspace]$ cat a.sh

. ./b.sh

echo "after source: b=$b"

b=1

echo "assign value: b=$b"

. ./b.sh

echo "after second source: b=$b"

[kodango@devops workspace]$ cat b.sh

b=0

b.sh的代码只有一行,仅仅是将变量b赋值成0。而在a.sh中,两次导入b.sh,同时在两次导入的中间会主动将b赋值为1。执行a.sh后,会得到以下的结果:

[kodango@devops workspace]$ sh a.sh

after source: b=0

assign value: b=1

after second source: b=0

这个结果并不太让人意外,因为正如我们之前说的,被source的脚本的代码会在导入的过程中被执行。在这里,每次导入b.sh的时候,变量b都会重新赋值成0。所以会设置默认值的脚本,在被source的时候,要非常注意这一点,当脚本被重复导入的时候,该变量的值会被重新赋值成默认值。

这个问题在我使用logdotsh的时候遇到了,logdotsh为了能够方便使用,对日志打印的行为设置了一些默认值,例如日志级别、日志格式等等,同时也提供了设置这些参数的函数。比如可以通过set_loglevel来调整默认的日志级别。这样就带来了一个问题,当你导入logdotsh脚本之后,通过set_loglevel设置了日志级别,但是如果重新再导入一次logdotsh之后,日志级别会被默认值覆盖。

所以必须找到一种方法,可以避免一个脚本被多次重复导入。我现在的做法是增加一个标志变量:当第一次导入logdotsh时会设置该变量,同时设置默认参数;第二次导入的时候检查该变量的值,如果不为空则不再设置默认值,具体的代码是这样的:

# Determines whether the default value have be set

if [ -z "$_log_set_default" ]; then

# Set the flag to 1

_log_set_default=1

# Show log whose level less than this

_log_level=3

fi

这种做法类似C/C++中避免头文件被多次引用的解决方法:

#ifndef _HEADERNAME_H

#define _HEADERNAME_H

// 头文件内容

#endif

这种做法比较简单,唯一需要关注的是标志变量_log_set_default必须要特别点,尽量保证不会在脚本的其它地方使用或者设置。下面的代码中,使用__sourced_$$__作为标志变量的名称,其中$$会被展开成当前脚本的pid,${!_sourced_}是间接引用。

[kodango@devops workspace]$ cat sourced.sh

_sourced_="__sourced_$$__"

echo "Flag variable $_sourced_=${!_sourced_}"

if [ -z "${!_sourced_}" ]; then

eval "$_sourced_=1"

echo "It is the first time to source script"

else

echo "The script have been sourced"

fi

接下来,我们在当前环境下多次导入上面的脚本:

[kodango@devops workspace]$ source sourced.sh

Flag variable __sourced_381__=

It iss the first time to source script

[kodango@devops workspace]$ source sourced.sh

Flag variable __sourced_381__=1

The script have been sourced

通过标志变量,现在我们就能避免一个脚本被多次导入了。不知道大家还有没有更好的方法来处理这种情况?

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值