Shell语法
变量:
在Shell中,我们在使用变量之前并不需要进行声明.相反我们可以在需要的时候进行简单的使用就可以了.在默认的情况下,所有的变量都是作为字符串进行存储的,虽然有时我们会用数字为其赋值.Shell以及其他的一些实用的转换程序会将数字字符串转换成相应的值为进行操作.在Linux系统中是要区分大小的,所以在Shell看来变量foo与Foo是不同的.
在Shell中我们要访问变量的值,我们要在变量前加上一个$.当我们要为变量赋值时,我们可以只使用变量的名字,Shell会在需要的时候进行动态创建.检测变量内容一个简单的办法就是在终端进行输出,这时要在变量前加上一个$.
在命令行中我们可以用下面的方法来设置和检测变量的值:
$ salutation=Hello
$ echo $salutation
Hello
$ salutation=”Yes Dear”
$ echo $salutation
Yes Dear
$ salutation=7+5
$ echo $salutation
7+5
我们还可使用read命令将用户的输入赋值给变量.这样就会将变量的名字作为参数并会等待用户的输入.read命令会在用户输入回车的时候结束.当从终端读入变量时我们并不需要使用引号.如下面的例子:
$ read salutation
Wie geht’s?
$ echo $salutation
Wie geht’s?
引号:
在继续我们的学习之前我们要清楚引号的作用.
通常脚本中的参数是由空白字符来分隔的,如空格,Tab或是回车.如果我们要我们的参数包含一个或是更多个参数,我们就要使用引号了.
例如变量$foo的行为要看我们使用的引号的类型了.如果我们是用双引号,在这一行执行时就会用他的值进行替换,而如果我们使用单引号就不会发生这样的替换.我们还可以使用转义字符/来除去$的特殊意义.
在通常的情况下,我们双引号来包含字符串,这样就可以防止变量被空白符所分隔,而且会用变量的值进行替换.
在下面的这个例子中我们就会看到引号对于变量输出的影响:
#!/bin/bash
myvar=”Hi there”
echo $myvar
echo “$myvar”
echo ‘$myvar’
echo /$myvar
echo Enter some text
read myvar
echo ‘$myvar’ now equals $myvar
exit 0
这个程序的输出为:
Hi there
Hi there
$myvar
$myvar
Enter some text
Hello World
$myvar now equals Hello World
工作原理
我们创建了变理myvar,并赋值为Hi there.变量的内容由命令echo显示出来,从而可以看出$字符扩展对变量内容的影响.从这输出我们可以看出双引号并不会影响变量的替换,而单引号和反斜线却会有这样的影响.我们同时使用一个read命令来从用户得到输入.
环境变量
当启动一个Shell脚本时,一些变量会由环境中的值进行初始化.在脚本中这些变量通常为大写字母,从而与用户定义的变量进行区分,而用户定义的变理常用小写字母来表示.创建的变量依赖于我们个人的配置.其中的许多列在手册页中,但是基本的一些列在下面的表中:
$HOME 当前用户的主目录
$PATH 用来进行命令查找的由冒号分隔的目录列表
$PS1 命令提示,通常为$,但是在bash中我们可以使用更为复杂的值.例如,字符串[/u@/h /W]$是流行的默认用法来告诉我们当前的用户,机器名称以及当前的工作目录,同时给出$提示.
$PS2 第二提示符,当提示额外的输入时使用,通常为>
$IFS 输入区域分隔符.当Shell读入输入时会使用一个字符列表来分隔输入的单词,通常是空格,tab和新行字符.
$0 Shell脚本的名称.
$# 传递的参数个数.
$$ 脚本的进程ID,通常用在一个脚本内部用来建立唯一的一个临时文件,如/tmp/tmp-file_$$.
如果我们的脚本调用一些参数,那么会建立一些其他的变量.即使没有传递参数,环境变量$#仍然存在,但是值却为0.
参数变量列在下面这个表中:
$1,$2,... 传递给脚本的参数.
$* 以单变量的形式显示所有的参数列表,由环境变量IFS中的第一个字符分隔.
$@ $*的一个灵巧变形.他并不使用IFS环境变量,所以如果IFS为空那么所有的所有的参数会一起运行.
我们可以通过下面的测试容易的看出$@和$*的区别:
$ IFS=’’
$ set foo bar bam
$ echo “$@”
foo bar bam
$ echo “$*”
foobarbam
$ unset IFS
$ echo “$*”
foo bar bam
正如我们所看到的,在双引号内,$@将参数进行分隔显示,而与IFS的值无关.通常来说,如果我们要访问参数,$@是一个很灵敏的选择.
我们不仅可以用echo命令打印出变量的内容,而且我们可以使用read命令来读取他们的内容.
参数和环境变量
下面的脚本展示了简单变量的处理.在我们输入了下面的脚本内容并保存为try_var,我们一定要记得用命令chmod +x try_var为其加上可执行权限.
#!/bin/sh
salutation=”Hello”
echo $salutation
echo “The program $0 is now running”
echo “The second parameter was $2”
echo “The first parameter was $1”
echo “The parameter list was $*”
echo “The user’s home directory is $HOME”
echo “Please enter a new greeting”
read salutation
echo $salutation
echo “The script is now complete”
exit 0
如果我们运行这个脚本我们会得到下面的输出:
~$ ./try_var.sh foo bar baz
Hello
The program ./try_var.sh is now running
The second parameter was bar
The first parameter list was foo bar baz
The user's home directory is /home/mylxiaoyi
Please enter a new greeting
hello
The script is now complete
工作原理:
这个脚本创建了一个名为salutation的变量并显示他的内空,然后显示了各种参数变量,而环境变量$HOME已经存在并且已经有适当的值.
函数:
所有程序语言的基本原则是测试条件并在这些测试的基础上进行各种不同的操作.在我们讨论这个话题之前,我们先来看一下在Shell脚本中我们会用到的函数构造以及我们要使用的控制结构.
一个Shell脚本可以测试由命令行调用的任何命令的返回代码,包括我们自己书写的脚本.这就是我们在每一个Shell脚本最后包含exit代码的重要原因.
test或[命令:
事实上,大多数的脚本大量的使用了Shell真假检测的test或是[命令.在大多数的系统上,[和test命令是同义的,但是当使用了一个[命令时而同时为了可读在末尾使用了一个]命令.使用[命令看起来有一点奇怪,但是这个命令在代码中会使得命令的语法看起来要简单,整洁,并且与其他的程序语言很相像.
ls -l /usr/bin/[
-rwxr-xr-x 1 root root 25040 2005-11-16 21:17 /usr/bin/[
我们会使用一个简单的测试例子来介绍test命令:检测一个文件是否存在.用于这个目的的命令是test -f <filename>,所以我们可以用下面的脚本:
if test -f fred.c
then
...
fi
我们也可以像下面的样子来写:
if [ -f fred.c ]
then
...
fi
test命令的返回代码(条件是否满足)决定于条件代码是否运行.
在这里我们要注意是我们必须在[和条件之间用空格进行分隔.我们可以用下面的方法来记住这一点:[是test命令的另一种写法,而我们要在test命令后输入空格.
如果我们喜欢将then与if放在同一行,我们必须要加一个冒号来与then进行分隔:
if [ -f fred.c ]; then
...
fi
我们可以用的test命令的条件类型有以下的三种:字符串比较,算术比较和文件条件.下面的三张表展示了这些条件类型:
字符串比较:
string1 = string2 如果相等则为真
string1 != string2 如果不等则为真
-n string 如果不空则为真
-z string 如果为空则为真
算术比较:
expression1 -eq expression2 如果相等则为真
expression1 -ne expression2 如果不等则为真
expression1 -gt expression2 如果大于则为真
expression1 -ge expression2 大于等于则为真
expression1 -lt expression2 如果小于则为真
expression1 -le expression2 小于等于则为真
!expression 如查为假则为真
文件:
-d file 如果为目录则为真
-e file 如果存在则为真(在这里要注意的是,由于历史原因,-e选项并不可移植,所以常用的是-f选项
-f file 如果为常规文件则为真
-g file 如果设置了组ID则为真
-r file 如果文件可读则为真
-s file 如果文件大小不为零则为真
-u file 如果设置了用户ID则为真
-w file 如果文件可写则为真
-x file 如果文件可执行则为真
现在我们似乎走得有一点的太前了,但是接下来的是一个例子.在这里我们要测试文件/usr/bash,这样我们就可以清楚的看到这些条件的用法:
#!/bin/sh
if [ -f /bin/bash ]
then
echo “file /bin/bash exists”
fi
if [ -d /bin/bash ]
then
echo “/bin/bash is a directory”
else
echo “/bin/bash is NOT a directory”
fi
在测试为真以前,所有的文件测试条件要法度文件存在.这个列表包含了test命令常用的选项,所以我们可查看手册页得到一个完全的信息.如果我们正在使用bash,而其中内嵌了test,我们可以用命令help test得到详细的信息.
shell控制结构
1.if语句
if语句用于条件控制结构中,其一般格式为:
if 测试条件
then 命令1
else 命令2
fi
其中,if、then、else和fi是关键字。例如:
if test -f "$1"
then echo "$1 is an ordinary file . "
else echo "$1 is not an ordinary file . "
fi
应该注意,if语句中else部分可以缺省。另外,if 语句的else部分还可以是else—if结构,此时可以用关键字“elif”代替“else if”。
通常,if的测试部分是利用test命令实现的。其实,条件测试可以利用一般命令执行成功与否来作判断。如果命令正常结束,则表示执行成功,其返回值为0,条件测试为真;如果命令执行不成功,其返回值不等于0,条件测试就为假。所以if的语句的更一般形式是:
if 命令表1
then 命令表2
else 命令表3
2.条件测试
条件测试有三种常用形式:一种是用test命令,如上所示。另一种是用一对方括号将测试条件括起来。这两种形式是完全等价的。例如,测试位置参数$1是否是已存在的普通文件,可写为test -f "$1"。也可写成[ -f "$1" ]。利用一对方括号表示条件测试时,在左方括号“[”之后、右方括号“]”之前各应有一个空格。
第三种形式是:
[[条件表达式]]
test命令可以和多种系统运算符一起使用。这些运算符可以分为四类:文件测试运算符(文件的属性及权限等)、字符串测试运算符(两个串是否相同及是否为空)、数值测试运算符(大小关系)和逻辑运算符(逻辑与、或、非)。
3.case语句
case语句允许进行多重条件选择。其一般语法形式是:
case 字符串 in
模式字符串1) 命令
…
命令;;
模式字符串2) 命令
…
命令;;
…
模式字符串n) 命令
…
命令;;
esac
其执行过程是用“字符串”的值依次与各模式字符串进行比较,如果发现同某一个匹配,那么就执行该模式字符串之后的各个命令,直至遇到两个分号为止。如果没有任何模式字符串与该字符串的值相符合,则不执行任何命令。
在使用case语句时应注意:
(1)每个模式字符串后面可有一条或多条命令,其最后一条命令必须以两个分号(即;;)结束。
(2)模式字符串中可以使用通配符。
(3) 如果一个模式字符串中包含多个模式,那么各模式之间应以竖线(|)隔开,表示各模式是“或”的关系,即只要给定字符串与其中一个模式相配,就会执行其后的命令表。
(4)各模式字符串应是惟一的,不应重复出现。并且要合理安排它们的出现顺序。例如,不应将“*”作为头一个模式字符串,因为“*”可以与任何字符串匹配,它若第一个出现,就不会再检查其它模式了。
(5)case语句以关键字case开头,以关键字esac(是case倒过来写!)结束。
(6)case的退出(返回)值是整个结构中最后执行的那个命令的退出值。若没有执行任何命令,则退出值为零。
4. while语句
Shell中有三种用于循环的语句,它们是while语句、for语句和until语句。
while语句的一般形式是:
while 测试条件
do
命令表
done
其执行过程是,先进行条件测试,如果结果为真,则进入循环体(do—done之间部分), 执行其中命令;然后再做条件测试……直至测试条件为假时才终止while语句的执行。例如:
while [ $1 ]
do
if [ -f $1 ]
then echo "display : $1 "
cat $1
else echo "$1 is not a file name . "
fi
shift
done
这段程序对各个给定的位置参数,首先判断其是否是普通文件,若是,则显示其内容;否则,显示它不是文件名的信息。每次循环处理一个位置参数$1,利用shift命令可把后续位置参数左移。
测试条件部分除使用test命令或等价的方括号外,还可以是一组命令。根据其最后一个命令的退出值决定是否进入循环体执行。
5.until语句
until语句的一般形式是:
until 测试条件
do
命令表
done
它与while语句很相似,只是测试条件不同:当测试条件为假时,才进入循环体,直至测试条件为真时终止循环。
6.for语句
for语句是最常用的建立循环结构的语句。其使用格式主要有三种,取决于循环变量的取值方式。
格式一:
for 变量 in 值表
do
命令表
done
例如:
for day in Monday Wednesday Friday Sunday
do
echo $day
done
其执行过程是,变量day依次取值表中各字符串,即第一次将“Monday”赋给day,然后进入循环体,执行其中的命令,显示出Monday。第二次将“Wednesday”赋给day,然后执行循环体中命令,显示出Wednesday。依次处理,当day把值表中各字符串都取过一次之后,下面day的值就变为空串,从而结束for循环。因此,值表中字符串的个数就决定了for循环执行的次数。在格式上,值表中各字符串之间以空格隔开。
格式二:
for 变量 in 文件正则表达式
do
命令表
done
其执行过程是,变量的值依次取当前目录下(或给定目录下)与正则表达式相匹配的文件名,每取值一次,就进入循环体执行命令表,直至所有匹配的文件名取完为止,退出for循环。
格式三:
for i in $* 或者 for i
do do
命令表 命令表
done done
这两种形式是等价的。其执行过程是,变量i 依次取位置参数的值,然后执行循环体中的命令表,直至所有位置参数取完为止。
7.break命令和continue命令
break命令可以使我们从循环体中退出来。其语法格式是:
break [ n ]
其中,n表示要跳出几层循环。默认值是1,表示只跳出一层循环。
continue命令跳过循环体中在它之后的语句,回到本层循环的开头,进行下一次循环。其语法格式是:
continue [ n ]
其中,n表示从包含continue语句的最内层循环体向外跳到第几层循环。默认值为1。循环层数是由内向外编号。