Shell学习笔记

最近在学习Shell脚本编程,因为工作环境已经转到了*nix下,不会熟练使用Shell显然是不行的。以前对于Shell的掌握程度只是会用几个十分基本的命令而已,根本不算会Shell这门“语言”。(之前Hackathon的时候要求用Github协同开发,我都是用的Github for Mac这个桌面应用,因为那时git的概念和基本命令都根本没弄懂和记住……跑题了。。接下来还会写一篇《Git学习笔记》。)

废话少说,边学边写,学到哪里写哪里,以后有空再补全。

1.常见的Shell解释器

我试过的有bash,sh,csh,zsh。现在正在用zsh,貌似Mac OS X 10.9.4已经自带了。zsh的功能和扩展性都十分强大,而且还有一个叫做oh-my-zsh的开源项目可以增强zsh的体验,具体特性和安装方法不说了,网上一搜一大把,很简单。总之用过的人据说都不会再用默认的bash了。

顺便说一句,有些语法细节和命令在不同Shell脚本解释器中还是不太一样的,比如echo {01..11}在OSX自带的bash中的运行结果如下:

在zsh(或GNU bash)中运行结果如下:

而在csh中就直接原封不动地输出了,如下:

可见不同Shell对于{数字..数字}这种序列生成方式的处理方法是不同的。另外,echo这个命令在不同的Shell中也不同,像zsh中的echo是一个内建命令,如下:

而bash中的echo就是调用的/bin/echo,如下:

这些只是我在试图编写Shell脚本来下载2013年全年的Github Archive时发现的小问题,同时感谢贵lab的彤姐和梓翔学长的耐心解答。

因此,当移植Shell脚本时可能需要注意一下在不同Shell中运行效果是否不同。最好在Shell脚本的第一行加一个指令来指定解释器,如

指定使用zsh运行该脚本。将zsh改为其他shell的名字(如bash),即可指定使用其他Shell(如bash)来运行。

2.常用语法

这篇文章不是教程,而是笔记,所以不会详细地从头开始介绍各种语法,而是只记录我觉得有用且不是一眼就能记住的语法。

2.1 变量和数组

Shell中的变量本质上都是字符串。

变量的长度(即字符的个数):${#variable}

Bash中数组的定义:array_name=(value0 value1 value2 value3),下标从0开始计数。

可以单独定义某个数组元素,如arr[22]=123。甚至可以将数组当成Python中的字典来使用,如arr[‘sss’]=ssssb。

读取数组某元素:${arr_name[subscript]}

获取所有元素:${arr_name[@]}或${arr_name[*]}

数组的长度(即数组中有多少元素):${#arr_name[@]}或${#arr_name[*]}。

注意:zsh中数组下标从1开始记,下标只能是数字,且数组长度等于最大的那个下标。

2.2 语句

Shell中的语句也是用分号;隔开的。输入echo 123; echo 456,回车,会分别执行两条语句,在两行内分别输出123和456。

不过单个语句末尾没必要加分号,加了跟不加一样。只有当在一行内写两个或多个语句时才需要加分号。

2.3 函数

函数定义很简单,方法如下:

function(){

命令1

命令2

……

return xxx

}

同样的,每行命令后面不需要加分号。加了也白加,没任何用。

注意1. function()的括号中不需要指明参数。可以向函数传入任意个参数,如function() 10 9 8 7 6 5 4 3 2 1。在函数中用${n}可以调用第n个参数。例:

注意2. return可有可无,需要函数有返回值时,就加return。不过这个返回值貌似只支持0~255,所以没多大用,不如用改变变量值或echo输出的方法来返回需要的值。顺便提一句,Shell中用$?表示最近一次函数(命令)调用的返回值。可以试试:

输出为88。

2.4 test或[

test和[都是用来检测条件是否成立的程序,两者功能一样,写法不一样。假设表达式是expression,那么两种用法分别为:

返回值若为0,表示expression为真,1表示expression为假或缺少表达式,>1表示出现错误。

判断文件的存在性和属性我比较喜欢用test,看起来舒服。test -e file是测试文件(夹)是否存在,-d是测试是否是文件夹,-f是测试是否是文件,-x测试是否是可执行的文件或可搜索的文件夹。其他的自己man去吧。

判断表达式是否成立我比较喜欢用[,因为它比较简洁。但是需要注意,用[的话一定不要丢了空格,不然是语法错误。其实这个很好理解,其实[是/bin/[,也就是/bin目录中的一个程序。我们在调用程序时都是先写程序名,然后是要传入的每个参数,每个参数前要有空格,以示分隔。比如git checkout master,运行的是名为git的程序,传给它的第一个参数是checkout,第二个参数是master。(其实还有第0个参数,是带路径的文件名,欲知详情可查阅C语言中对于argc、argv[]的解释。又扯远了。。)对于[这个程序呢,它的最后一个参数必须是],这样让人看起来像个用括号括起来的条件似的,其实只是个程序调用而已。我们看[ 3 -eq 5 ],[是程序名,3,-eq,5,],都是传入的参数,3是要比较的第一个值,5是要比较的第二个值,-eq是一种比较参数,表示比较两个数是否相等,此外还有-lt(小于)之类的;]表示输入结束。我们来测试两组输入:

输出符合前述规则。

[的那几个参数其实也很好记,equal是-eq,not equal是-ne,greater than是-gt,greater than or equal是-ge,less than 是-lt,less than or equal是-le。

2.5 if语句和条件

if语句(其他语句也是)中的condition可以是test或[,也可以直接写true或false(相当于没判断。。)。Shell中还支持C语言风格的条件,如(($n<10))。(注意是两层括号)

标准形式:

写在一行里:

2.6 case语句

感觉Shell里面的命令是很讲究对称的,比如if的结束标记是fi,而case的结束标记就是esac,就是简单地倒过来。Shell中的case语句和C语言中的switch语句很像,直接上模板:

说明:

(1)pattern是匹配模式,可以为普通的值,可以是用方括号表示的连续的范围(如[a-z],[A-Z],[0-9]),可以是用|隔开的多个值或范围。如fun(){read n; case $n in [0-9]|[a-z]) echo ‘digit or letter’;; [A-Z]) echo ‘capital letter’;; *) echo ‘default’;; esac}(没错,我把能省略的空格、回车和分号都省略了)在输入0~9或a~z时输出digit or letter,在输入A~Z时输出capital letter,都不符合时输出default。

(2)每个pattern(包括用*表示的默认情况)的语句块末尾都要有两个分号;;表示结束。

2.7 循环

for 

var不用加$,list可以是{a..z},可以是$(seq $n)(或seq $n,表示1~n的序列),也可以是几个用空格隔开的字符串,如:for n in 1 02 3 04 5。简写:for var in list; do statement; done。

bash中还支持C语言风格的for循环(还是两层括号):for((i=$start;i<=$end;i++));do statements;done。双层括号里面的i不用加$,statements里面调用i需要加$。

while

condition和上面if语句中的condition写法是一样的。

记录三种自增方式:((i++));i=$(($i+1));i=expr $i + 1(这个要注意符号和+左右的空格)。顺便说一句,Shell里面的++和--也有前缀和后缀形式,可以试一下i=0;echo $((i++)); echo $((++i)); echo $((i--)); echo $((--i)); 。

until

形式就是把while循环中的while换成until,意义自己领会。

break和continue

用法与C语言中没有区别,不再赘述。

3.常用命令(程序)

3.1 man

3.2 ls

3.3 grep

3.4 find

3.5 cat

3.6 vi

3.7 awk

4.一些细节

4.1 ()与`

记这么多,基本的Shell编程应该是够用了。后面的有空慢慢修改,就先这样吧。

发表评论

电子邮件地址不会被公开。 必填项已用*标注