ForeWord
Question:
如果把C语言的各种数据类型、指针、结构体、联合体等结构全都去掉,还剩下什么?
……
没错,还有条件判断、循环、一维数组(因为没有指针)以及部分函数。
而这些,正是shell语法结构中包含的所有内容。
所以我们可以看出,shell语法学习起来远比C语言容易得多。
那么本文就简单总结下shell语法的内容及其具体使用方法。
Key Points:
- 条件判断
- 循环语句
- 位置参数和特殊变量
- Shell脚本调试选项
- 函数
数组
~tips:全文阅读需10min~
Part 1:条件判断
1. test & [
首先你没有看错,[ 也是一个命令。它和test命令都被用来测试条件是否成立,使用方法和场景如下:
1.测试数字和字符串
先编写脚本程序test.sh:
然后sh test.sh运行:
这里的 ] 是 [命令的参数。
其中各选项解释如下:
选项 | 含义 |
---|---|
-eq(equal) | 等于 |
-ne(not equal) | 不等于 |
-gt(great) | 大于 |
-ge | 大于等于 |
-lt(little) | 小于 |
-le | 小于等于 |
有没有发现,这里的判断结果与C语言刚好相反呢?
没错,用test和[判断时,如果测试结果为真,返回0;结果为假则返回1.
为什么要这样设置呢?
shell脚本中,所有的语句都被看作一条命令,它只关心命令执行的对不对。所以条件判断时并不直接看语句是否成立,而是要看命令的退出码(Exit Status)。在linux中如果命令执行正确,返回码就是0,所以没有必要专门为条件判断改变这一习惯。进而才有结果为真返回0的特点。
此外,还可以看出:命令test或 [ 的参数形式是相同的,只不过test命令不需要]参数。
字符串还可以这样判断:
运行:
还有这样:
运行:
由于[比test更简洁。所以一般推荐使用[来进行条件测试,当然,如果你喜欢用test,我也没意见。。。
2.其它选项
除了判断数字和字符串以外,还可以判断目录类型以及其他的东西。这也自然涉及到了目录类型的选项。具体见下例:
选项当然远远不止这些,光目录的选项就有很多,大家可以自行百度了解。但用法就如上例。
3. 与、或、非的测试命令
逻辑上的与或非也是条件测试的重点,用法如下:
运行结果:
2. if/then/elif/else/fi
和C语言类似,在Shell中用if、then、elif(elseif)、else、fi这几条命令实现分支控制。这种流程控制语句本质上也是由若干条Shell命令组成的。
注意:
1.如果两条命令写在同行则需要用;号隔开
2.then后面有换行,若不换行,Shell会认为这条命令没写完进而自动续行,把下一行接在then后面当作一条命令处理。
3.和[命令一样,要注意命令和各参数之间必须用空格隔开。
4.if命令的参数组成一条子命令为真则执行then后面的子命令,否则执行elif、else或者fi后面的子命令。
5.if后面的子命令通常是测试命令,但也可以是其它命令。
6.Shell脚本没有{}括号,所以用fi表示if语句块的结束。
7.分支语句内容不能为空,否则会出错。
如果你实在什么都不想写,可以在if后加:表示空语句
此外,,Shell还提供了&&和||语法,作用类似于if语句。
其中,&&相当于“if…then…”,而||相当于“ifnot…then…”。
&&和||用于连接两个命令,而上面讲的-a 和-o仅用于在测试表达式中连接两个测试条件,要注意它们的区别:
3. case/esac
case命令可类比C语言的switch/case语句,esac表示case语句块的结束。不同的是:
1. C语言的case只能匹配整型或字符型常量表达式,而Shell脚本的case可以匹配几乎任意类型,且每个匹配分支可以有若干条命令,末尾必须以;;结束。
2. 语句执行时找到第一个匹配的分支并执行相应的命令,然后直接跳到esac之后,不需要像C语言一样用break跳出。
Part 2:循环语句
1. for/do/done
Shell脚本的for循环结构和C语言很不一样,使用方法有2种:
个人推荐第二种,因为习惯了。
2. while/do/done
Shell中的while循环也类似C语言。但用法上依然略有不同:
3. until
Shell中的until循环相当于C语言的do..while循环,使用方法如下:
Part 3:位置参数和特殊变量
shell中定义了脚本文件的未知参数并对多个特殊变量进行自动赋值,使他们表示特定的含义,这对编写脚本程序带来了很多益处。
1. $系列
最常用的就是下表中给出的项目:
项目 | 含义 |
---|---|
$0 | 相当于C语⾔main函数的argv[0] |
$1/2… | 位置参数,相当于C语⾔main函数的argv[1]、argv[2] |
$# | 位置参数的个数,相当于C语⾔main函数的argc-1,注意这⾥的#后⾯不表示注释 |
$@ | 表示整个位置参数列表 |
$? | 上一条命令的退出码 |
$$ | 当前shell脚本的进程号 |
2. shift命令
位置参数可以⽤shift命令左移。且只将位置参数左移,前边移出的抛弃。$0不变。例如:
Part 4:shell调试选项
Shell提供了一些用于调试脚本的选项,如下表所示:
选项 | 作用 |
---|---|
-v | 读⼀遍脚本中的命令但不执⾏,⽤于检查脚本中的语法错误 |
-x | 提供跟踪执⾏信息,将执⾏的每⼀条命令和结果依次打印出来 |
使⽤这些选项有三种⽅法(以-x为例)
⼀是在命令行提供参数$ sh -x test.sh:
⼆是在脚本开头提供参数! /bin/sh -x:
第三种方法是在脚本中⽤set命令启⽤或禁⽤参数:
其中,set -x和set +x分别表⽰启用和禁⽤-x参数,这样可以只对脚本中的某⼀段进⾏跟踪调试。
Part 5:函数
和C语言类似,Shell中也有函数的概念。但是函数定义中没有返回值也没有参数列表。
注意函数体的左花括号 { 和后面的命令之间必须有空格或换行,如果将最后⼀条命令和右花括号 } 写 在同⼀行,命令末尾必须有;号。
Shell中的函数调用不写括号才就可以执⾏函数体中的命令。
Shell脚本中的函数必须先定义后调用,⼀般把函数定义都写在脚本的前⾯,把函数调⽤和其它命令写在脚本的最后(类似C语⾔中的main函数,这才是整个脚本实际开始执⾏命令的地⽅)
Shell函数没有参数列表并不表示不能传参数,事实上,函数就像是迷你脚本,调⽤函数时可以传任意个参数,即用上面讲到的位置参数来提取参数,函数中的位置参数相当于函数的局部变量,改变这些变量并不会影响函数外⾯的$0/1/2等变量。
函数内部定义的变量并非C语言的局部变量,而更像是在此脚本文件内全局有效的变量。在函数体外也可以访问,如上例中的sum:
…
如果想要让它起到局部变量的作用,就要加local关键字:
此时,在从外部访问就看不到它的值了:
另外,关于函数退出码的判定,有以下两种方法:
最后再给大家安利一行shell代码:
可以记住这条命令,看谁不爽发给谁。。。
Part 6:数组
因为没有指针,所以bash只支持一维数组,但没有限定数组的大小。
类似于C语言,数组元素的下标由0开始编号。获取数组中的元素要利⽤下标,下标可以是整数或算术表达式,其值应⼤于或等于0。
- 在Shell中,⽤括号来表⽰数组,数组元素⽤“空格”符号分割开
- 还可以单独定义数组的各个分量,而且可以不使⽤连续的下标。
- 读取数组元素值的⼀般格式是: ${array_name[index]}
- 使⽤@ 或 * 可以获取数组中的所有元素
遍历数组元素:
运行:
The End
终于写完啦,最后再给大家安利两个链接,讲了shell脚本中单引号,双引号和反引号的区别,可以了解下:
http://blog.youkuaiyun.com/hittata/article/details/8049665
http://blog.youkuaiyun.com/miyatang/article/details/8077123