目录
一 shell中的位置变量
$0
:表明脚本的名字,比如执行./test/test.sh 那么$0=./test/test.sh
;
$#
:表示传递到脚本的参数的个数;
$*,$@
:两者都是指所有传递到脚本的参数;
$$
:这个是脚本运行的pid,跟参数好像没有关系;
$?
:命令的退出状态,也就是命令结果码的保存位置;
$n(n=1,2,3…)
:执行脚本时后面所跟的参数,执行./start.sh arg1 arg2
,则$1
的值为arg1
,$2
的值为arg2
。
#!/bin/bash
index=1
for arg in "$@"
do
echo "arg $index=$arg"
let "index+=1"
done
index=1
for arg in "$*"
do
echo "arg $index=$arg"
let "index+=1"
done
index=1
for arg in $@
do
echo "arg $index=$arg"
let "index+=1"
done
index=1
for arg in $*
do
echo "arg $index=$arg"
let "index+=1"
done
./test.sh 1 2 3 4
的运行结果:
./test 1 "2 3" 4
的结果:
二 shell的条件语句
1 if else
#!/bin/bash
#实现:对100分成绩分等级
echo "input:"
read line
if [ "$line" -gt 100 ] || [ "$line" -lt 0 ];then
echo "error number"
exit 0
fi
if [ "$line" -gt 80 ];then
echo "A"
elif [ "$line" -gt 70 ]
then
echo "B"
elif [ "$line" -gt 60 ]
then
echo "C"
else
echo "D"
fi
2 比较
文件比较运算符
-e filename 如果 filename 存在,则为真 [ -e /var/log/syslog ]
-d filename 如果 filename 为目录,则为真 [ -d /tmp/mydir ]
-f filename 如果 filename 为常规文件,则为真 [ -f /usr/bin/grep ]
-L filename 如果 filename 为符号链接,则为真 [ -L /usr/bin/grep ]
-r filename 如果 filename 可读,则为真 [ -r /var/log/syslog ]
-w filename 如果 filename 可写,则为真 [ -w /var/mytmp.txt ]
-x filename 如果 filename 可执行,则为真 [ -L /usr/bin/grep ]
filename1-nt filename2 如果 filename1比 filename2新,则为真 [ /tmp/install/etc/services -nt /etc/services ]
filename1-ot filename2 如果 filename1比 filename2旧,则为真 [ /boot/bzImage -ot arch/i386/boot/bzImage ]
字符串比较运算符 (请注意引号的使用,这是防止空格扰乱代码的好方法)
-z string 如果 string长度为零,则为真 [ -z “
m
y
v
a
r
"
]
−
n
s
t
r
i
n
g
如果
s
t
r
i
n
g
长度非零,则为真
[
−
n
"
myvar" ] -n string 如果 string长度非零,则为真 [ -n "
myvar"]−nstring如果string长度非零,则为真[−n"myvar” ]
string1= string2 如果 string1与 string2相同,则为真 [ “
m
y
v
a
r
"
=
"
o
n
e
t
w
o
t
h
r
e
e
"
]
s
t
r
i
n
g
1
!
=
s
t
r
i
n
g
2
如果
s
t
r
i
n
g
1
与
s
t
r
i
n
g
2
不同,则为真
[
"
myvar" = "one two three" ] string1!= string2 如果 string1与 string2不同,则为真 [ "
myvar"="onetwothree"]string1!=string2如果string1与string2不同,则为真["myvar” != “one two three” ]
算术比较运算符
num1-eq num2 等于 [ 3 -eq $mynum ]
num1-ne num2 不等于 [ 3 -ne $mynum ]
num1-lt num2 小于 [ 3 -lt $mynum ]
num1-le num2 小于或等于 [ 3 -le $mynum ]
num1-gt num2 大于 [ 3 -gt $mynum ]
num1-ge num2 大于或等于 [ 3 -ge $mynum ]
-eq
:等于;(equal)
-ne
:不等于;(not equal)
-gt
: 大于;(greater than)
-lt
: 小于;(less than)
-ge
: 大于等于;(greater than or equal to)
-le
: 小于等于。(less than or equal to)
-z
:判断变量的值,是否为空; zero = 0。
在shell中对比字符串和对比数字,要分开两种方式。对比字符串只能使用==
、<
、>
、!=
、-z
、-n
。对比字符串时,末尾一定要加上x(或者a、b等)一个字符,因为if [ $1x == "ab"x ]
时如果没有了x ,并且$1是"“,这个语句会翻译成if [ == "ab" ]
,左边相当于没有东西了,会报语法错误。或者使用[[ ]],就不需要x了。使用<或者>时,如果是用[ ],需要用转义符”",如>。
对比数字使用既能使用-eq、-ne、-gt、-ge、-lt、-le,也能使用==、<、>、!=。其中-eq的意思是equal,-ne是unequal,-gt是greater than,-ge是greater than or equal to,-lt是less than,-le是less than or equal to。
if [ -z $os_mem_size ]
三 case
case 语句的语法如下:
case $变量名 in
“值 1”)
;;
如果变量的值等于值1,则执行程序1
“值2”)
如果变量的值等于值2,则执行程序2
;;
……
;;
*)
如果变量的值都不是以上的值,则执行此程序
;;
esac
四 循环语句 for while until
#!/bin/bash
#循环 for
for i in 1 2 3 4 5
do
echo "i = $i"
done
# $(ls)执行命令ls
for i in $(ls)
do
echo "i = $i"
sleep 1
done
#!/bin/bash
#输入密码,只有三次机会输入
i=1
while [ : ]
do
echo "input password:"
read pass
if [ "$pass" = "123456" ]
then
echo "success"
break
fi
if [ "$i" -eq 3 ]
then
echo "falied"
exit 0
fi
let "i++"
done
#循环4次,每打印i的值睡一秒,i++
i=1
while [ "$i" -lt 5 ]
do
echo "i = $i"
sleep 1
let "i++"
done
五 set -e
你写的每个脚本都应该在文件开头加上set -e
,这句语句告诉bash如果任何语句的执行结果不是true则应该退出。
这样的好处是防止错误像滚雪球般变大导致一个致命的错误,而这些错误本应该在之前就被处理掉。如果要增加可读性,可以使用set -o errexit
,它的作用与 set -e
相同。
六 Makefile 的规则
target ... : prerequisites ...
[tab]command #命令前面为tab键
...
target是一个目标文件,可以是 Object File,执行文件,还可以是一个标签(Label)。prerequisites是生成 target 所需要的文件或是目标。command是执行的命令。例如:
edit : main.o kbd.o command.o
cc -o edit main.o kbd.o command.o
main.o : main.c defs.h
cc -c main.c
clean :
rm edit main.o kbd.o command.o
Makefile 里主要包含了五个东西:显式规则、隐晦规则、变量定义、文件指示和注释。
1、显式规则。说明了如何生成一个或多的的目标文件。这是由 Makefile 的书写者明显指出,要生成的文件,文件的依赖文件,生成的命令。
2、隐晦规则。由于我们的 make 有自动推导的功能,所以隐晦的规则可以让我们比较粗糙地简略地书写 Makefile,这是由 make 所支持的。
3、变量的定义。在 Makefile 中我们要定义一系列的变量,变量一般都是字符串,这个有点你 C 语言中的宏,当 Makefile 被执行时,其中的变量都会被扩展到相应的引用位置上。
4、文件指示。其包括了三个部分,一个是在一个 Makefile 中引用另一个 Makefile,就像 C语言中的 include 一样;另一个是指根据某些情 况指定 Makefile 中的有效部分,就像 C 语言中的预编译#if 一样;还有就是定义一个多行的命令。
5、注释。 Makefile 中只有行注释,和 UNIX 的 Shell 脚本一样,其注释是用#
字符。如果你要在你的 Makefile 中使用#
字符,可以用反斜框进行转义,如: \#
。 最后,还值得一提的是,在 Makefile 中的命令,必须要以[Tab]键开始。
七 自动推导
1 使用变量(减少.o)
objects = main.o kbd.o command.o
edit : $(objects)
cc -o edit $(objects)
main.o : main.c defs.h
cc -c main.c
kbd.o : kbd.c defs.h command.h
cc -c kbd.c
command.o : command.c defs.h command.h
cc -c command.c
用变量 object 代替 .o 文件。
2 C文件的推导
objects = main.o kbd.o command.o
edit : $(objects)
cc -o edit $(objects)
main.o : defs.h
kbd.o : defs.h command.h
command.o : defs.h command.h
.PHONY : clean
clean :
rm edit $(objects)
这种方法,也就是 make 的“隐晦规则”。上面文件内容中,.PHONY
意思表示clean
是一个“伪目标”。
3 头文件的推导
objects = main.o kbd.o command.o display.o insert.o search.o files.o utils.o
edit : $(objects)
cc -o edit $(objects)
$(objects) : defs.h
kbd.o command.o files.o : command.h
display.o insert.o search.o files.o : buffer.h
.PHONY : clean
clean :
rm edit $(objects)
八 变量和运算符
1 变量
$@
:目标文件,$^
:所有的依赖文件,$<
:第一个依赖文件,$()
表示引用括号内变量的值。
main:main.o mytool1.o mytool2.o
gcc -o $@ $^
main.o:main.c mytool1.h mytool2.h
gcc -c $<
在makefile的规则命令行中使用$var
就是在命令中引用makefile的变量,这里仅仅是读取makefile的变量然后扩展开,将其值作为参数传给了一个shell命令;而$$var
是在访问一个shell命令内定义的变量,而非makefile的变量。
2 运算符
=
:最基本的赋值,make会将整个makefile展开后,再决定变量的值。例如:y的值是 xyz bar,而不是 foo bar。
x = foo
y = $(x) bar
x = xyz
:=
:覆盖之前的值,变量的值决定于它在makefile中的位置,而不是整个makefile展开后的最终值。例如:y的值将会是 foo bar,而不是 xyz bar。
x := foo
y := $(x) bar
x := xyz
?=
:是如果没有被赋值过就赋予等号后面的值。+=
:是添加等号后面的值。@$()
:中的 @ 的含义是:在终端不输出该命令行。
3 wildcard
九 指定目标
1 all的作用
make
默认的执行第一个目标,由于makefile是根据最后的关联来执行命令的,所以不会执行无关的第二个目标命令,但是有的时候,比如只需要编译,而不需要连接,就不会形成关联。这中情况下,就需要all,如下会将所有的目标都执行了。
all : hello another
hello : hello.cpp
g++ -o $@ $<
another : another.cpp
g++ -o $@ $<
2 指定目标
可以指示make,让其完成你所指定的目标。要达到这一目的很简单,需在make命令后直接跟目标的名字就可以完成,任何在makefile中的目标都可以被指定成终极目标,但是除了以-
打头,或是包含了=
的目标,因为有这些字符的目标,会被解析成命令行参数或是变量。甚至没有被我们明确写出来的目标也可以成为make的终极目标,也就是说,只要make可以找到其隐含规则推导规则,那么这个隐含目标同样可以被指定成终极目标。
3 MAKECMDGOALS
有一个 make 的环境变量叫MAKECMDGOALS
,存放指定的终极目标的列表,如果命令行没有指定目标,那么变量是空值。这个变量可以使用在一些比较特殊的情形下。例如:
sources = foo.c bar.c
ifneq ( $(MAKECMDGOALS),clean)
include $(sources:.c=.d)
endif
如果输入的命令不是make clean
,那么 makefile 会自动包含foo.d
和bar.d
这两个文件。
4 clean
清空目标文件的规则:每个 Makefile 中都应该写一个清空目标文件(.o 和执行文件)的规则,这不仅便于重编译,也很利于保持文件的清洁。
clean:
rm edit $(objects)
更为稳健的做法是:
.PHONY : clean
clean :
-rm edit $(objects)
而在rm
命令前面加了一个小减号的意思就是,也许某些文件出现问题,但不要管,继续做后面的事。当然, clean 的规则不要放在文件的开头,不然,这就会变成 make 的默认目标。不成文的规则是——clean 从来都是放在文件的最后。
十 Makefile 的文件名
默认的情况下, make 命令会在当前目录下按顺序找寻文件名为GNUmakefile
、makefile
、Makefile
的文件,找到了解释这个文件。在这三个文件名中,最好使用Makefile
这个文件名,因为,这个文件名第一个字符为大写,这样有一种显目的感觉。最好不要用GNUmakefile
,这个文件是 GNU 的 make 识别的。有另外一些 make 只对全小写的makefile
文件名敏感,但是基本上来说,大多数的 make 都支持makefile
和Makefile
。如果自定义名称,可以使用 make 的参数- f
和--file
指定文件。
十一 shell
Makefile本质上来讲也是shell脚本,即每条command都是shell进程,运行完shell进程都会退出。
十二 引用其它的 Makefile
在 Makefile 使用 include 关键字可以把别的 Makefile 包含进来,被包含的文件会原模原样的放在当前文件的包含位置。 include 的语法是:
include <filename>
:filename 可以是当前操作系统 Shell 的文件模式(可以保含路径和通配符)在 include 前面可以有一些空字符,但是绝不能是[Tab]键开始。 include 和;可以用一个或多个空格隔开。 举个例子,你有这样几个 Makefile: a.mk、 b.mk、 c.mk,还有一个文件叫 foo.make,以及一个变量$(bar),其包含了 e.mk 和 f.mk,那么,下面的语句:
include foo.make *.mk $(bar)
等价于:
include foo.make a.mk b.mk c.mk e.mk f.mk
make 命令开始时,会把找寻 include 所指出的其它 Makefile,并把其内容安置在当前的位置。就好像 C/C++的#include 指令一样。 如果文件都没有指定绝对路径或是相对路径的话, make会在当前目录下首先寻找,如果当前目录下没有找到,那么, make 还会在下面的几个目录下找:
1、如果 make 执行时,有-I
或--include-dir
参数,那么 make 就会在这个参数所指定的目录下去寻找。
2、如果目录;/include(一般是: /usr/local/bin 或/usr/include)存在的话, make 也会去找。如果有文件没有找到的话, make 会生成一条警告信息,但不会马上出现致命错误。它会继续载入其它的文件,一旦完成 makefile 的读取, make 会再 重试这些没有找到,或是不能读取的文件,如果还是不行, make 才会出现一条致命信息。如果你想让 make 不理那些无法读取的文件,而继续执行,你可以在 include 前加一个减号-
。如:-include ;其表示,无论 include 过程中出现什么错误,都不要报错继续执行。和其它版本 make 兼容的相关命令是 sinclude,其作用和这一个是一样的。
十三 make参数
不需要 makefile 中的规则执行起来,只想检查一下命令,或是执行的序列,可以使用 make 命令的下述参数:-n
,–just-print
,–dry-run
,–recon
。
不执行参数,这些参数只是打印命令,不管目标是否更新,把规则和连带规则下的命令打印出来,但不执行,这些参数对于我们调试makefile很有用处:-t
,–touch
-q
,–question
:把目标文件的时间更新,但不更改目标文件。也就是说,make假装编译目标,但不是真正的编译目标,只是把目标变成已编译过的状态。
这个参数的行为是找目标的意思,也就是说,如果目标存在,那么其什么也不会输出,当然也不会执行编译,如果目标不存在,其会打印出一条出错信息。-W <file>
,–what-if=<file>
,–assume-new=<file>
,–new-file=<file>
这个参数需要指定一个文件。一般是是源文件(或依赖文件),Make会根据规则推导来运行依赖于这个文件的命令,一般来说,可以和-n
参数一同使用,来查看这个依赖文件所发生的规则命令。结合-p
和-v
来输出makefile被执行时的信息。
十四 Scons
make 这个工具自上个世纪 70 年代 Stuart Feldman 在贝尔实验室开发出以来,就一直是类 UNIX 程序员的最爱之一。
通过检查文件的修改时间,make 工具可以知道编译目标文件所要依赖的其他文件。在复杂的项目中,如果只有少数几个文件修改过,make 工具知道仅仅需要对哪些文件重新编译就可以确保目标程序被正确的编译链接。这样做的好处就是在编译中,不仅可以节省大量的重复输入,还可以确保程序可以被正确的链接,缩短编译的时间。
虽然如此,但是为 make 工具编写建造规则却不是一件容易的事。它复杂的配置规则,即使是有经验的开发者也望而生畏。
那么,今天介绍一个它的同类产品,也可以说是升级产品:Scons,它做的事情跟make一样,但更简单,更容易。
Scons是一个开放源码、以Python语言编码的自动化构建工具,可用来替代make编写复杂的makefile。并且scons是跨平台的,只要scons脚本写的好,可以在Linux和Windows下随意编译。
SCons 的设计目标就是让开发人员更容易、更可靠和更快速的建造软件。
与传统的 make 工具比较,SCons 具有以下优点:
使用 Python 脚本做为配置文件,对于 C,C++ 和 Fortran, 内建支持可靠自动依赖分析 . 不用像 make 工具那样需要 执行"make depends"和"make clean"就可以获得所有的依赖关系。
内建支持 C, C++, D, Java, Fortran, Yacc, Lex, Qt,SWIG 以及 Tex/Latex。 用户还可以根据自己的需要进行扩展以获得对需要编程语言的支持。
支持 make -j
风格的并行建造。相比 make -j
,SCons 可以同时运行 N 个工作,而 不用担心代码的层次结构。使用 Autoconf 风格查找头文件,函数库,函数和类型定义。良好的夸平台性。SCons 可以运行在 Linux, AIX, BSD, HP/UX, IRIX, Solaris, Windows, Mac OS X 和 OS/2 上。