大家都知道在Makefile可以调用shell脚本,但是Makefile和shell脚本是不同的。本文试着介绍一下Makefile和shell脚本的不同。

1、在Makefile中只能在target中调用Shell脚本,其他地方是不能输出的。比如如下代码就是没有任何输出:

VAR="Hello" 

echo "$VAR" 

 all: .....

以上代码任何时候都不会输出,没有在target内,如果上述代码改为如下:

VAR="Hello" 

 all: 

       echo "$VAR" .....

以上代码,在make all的时候将会执行echo命令。

2、在Makefile中执行shell命令,一行创建一个进程来执行。这也是为什么很多Makefile中有很多行的末尾都是“;  \”,以此来保证代码是一行而不是多行,这样Makefile可以在一个进程中执行,例如:

SUBDIR=src example 

all: 

      @for subdir in $(SUBDIR); \ 

      do\ 

       echo "building "; \ 

       done

上述可以看出for循环中每行都是以”; \”结尾的。

3、Makefile中所有以$打头的单词都会被解释成Makefile中的变量。如果你需要调用shell中的变量(或者正则表达式中锚定句位$),都需要加两个$符号($$)。实例如下:

PATH="/data/" 

 all: 

     echo ${PATH} 

     echo $$PATH

例子中的第一个${PATH}引用的是Makefile中的变量,而不是shell中的PATH环境变量,后者引用的事Shell中的PATH环境变量。

     以上三点的是Makefile调用shell应该注意的地方,写Makefile一定要注意。

 

shell脚本条件判断

 

UNIX Shell 编程中条件判断是极为重要的,以下是常用的条件判断:

-b file 若文件存在且是一个块特殊文件,则为真
-c file 若文件存在且是一个字符特殊文件,则为真
-d file 若文件存在且是一个目录,则为真
-e file 若文件存在,则为真
-f file 若文件存在且是一个规则文件,则为真
-g file 若文件存在且设置了SGID位的值,则为真
-h file 若文件存在且为一个符合链接,则为真
-k file 若文件存在且设置了”sticky”位的值
-p file 若文件存在且为一已命名管道,则为真
-r file 若文件存在且可读,则为真
-s file 若文件存在且其大小大于零,则为真
-u file 若文件存在且设置了SUID位,则为真
-w file 若文件存在且可写,则为真
-x file 若文件存在且可执行,则为真
-o file 若文件存在且被有效用户ID所拥有,则为真

-z string 若string长度为0,则为真
-n string 若string长度不为0,则为真
string1 = string2 若两个字符串相等,则为真
string1 != string2 若两个字符串不相等,则为真

int1 -eq int2 若int1等于int2,则为真
int1 -ne int2 若int1不等于int2,则为真
int1 -lt int2 若int1小于int2,则为真
int1 -le int2 若int1小于等于int2,则为真
int1 -gt int2 若int1大于int2,则为真
int1 -ge int2 若int1大于等于int2,则为真

!expr 若expr为假则复合表达式为真。expr可以是任何有效的测试表达式
expr1 -a expr2 若expr1和expr2都为真则整式为真
expr1 -o expr2 若expr1和expr2有一个为真则整式为真

特殊变量

$0 正在被执行命令的名字。对于shell脚本而言,这是被激活命令的路径
$n 该变量与脚本被激活时所带的参数相对应。n是正整数,与参数位置相对应($1,$2…)
$# 提供脚本的参数号
$* 所有这些参数都被双引号引住。若一个脚本接收两个参数,$*等于$1$2
$@ 所有这些参数都分别被双引号引住。若一个脚本接收到两个参数,$@等价于$1$2
$? 前一个命令执行后的退出状态
$$ 当前shell的进程号。对于shell脚本,这是其正在执行时的进程ID
$! 前一个后台命令的进程号

 

Shell函数
mylog() {

    echo "[`date '+%F %T'`] [ $2.$1 ] $3" >> $4
}

s_time=`date +%s`
s_curren_dir=$(pwd)
s_logfile=$s_curren_dir/log/install.log.`date '+%Y%m%d'`
logID=001
mylog "$s_time" "$logID" "your messager." "$s_logfile" #shell脚本中函数的调用不能用(),并且参数使用空格分隔,不是符合一般的习惯

 

 在这自定义函数我没试过,操一段

其实定义起来很简单, 没有我想象中的 def, command, function 之类的关键字, 而是等同于变量定义, 下面是尝试在实际项目中使用的函数定义:

[zrf@DMLinux pes]$ cat Makefile.common
# The following function make the input token uniq.
# the token may be not in the input order
# usage: uniq_result=$(call uniq, A B A B B C)
# expected result: uniq_result is A B C
uniq=$(shell echo $1 | sed 's/[ \t]\+/\n/g' | sed 's/^/ /' | sort -u | tr -d '\n')

# The following function link the -lnet* and -lwrap library statically while
# keep other libraries being link dynamically by wrapping the -lxxx option with
# -Wl,-Bstatic ... -Wl,-Bdynamic
link_some_lib_staticlly=$(shell echo $1 | sed 's/-l\(net[^ \t]*\|wrap\)\( -l\(net[^ \t]*\|wrap\)\)*/-Wl,-Bstatic & -Wl,-Bdynamic/g')



自定义函数的关键是一定要用=号, 而不是:=, :=与=的差别是:=是即时求值, =则是使用时才求值. 把:看作一个站着的小人, :=可以看作"立等可取"来助记.

命令定义中可以用$1, $2, $3来引用传递的参数, 注释里已经有了自定义函数的使用
call是个特殊的make函数, 用于调用其它函数, 参数以逗号分隔.

借助这种方法, 可以让那些频繁而又重复的操作被封装进一个函数里, 不需要到处复制, DRY远处不在.