shell教程
Shell 是一个用C语言编写的程序,它是用户使用Linux的桥梁。Shell既是一种命令语言,又是一种程序设计语言。
Shell 是指一种应用程序,这个应用程序提供了一个界面,用户通过这个界面访问操作系统内核的服务。
Ken Thompson的sh是第一种Unix Shell,Windows Explorer是一个典型的图形界面Shell。
Shell 脚本
Shell 脚本(shell script),是一种为shell编写的脚本程序。
业界所说的shell通常都是指shell脚本,但读者朋友要知道,shell和shell script是两个不同的概念。
由于习惯的原因,简洁起见,本文出现的"shell编程"都是指shell脚本编程,不是指开发shell自身。
Shell 环境
Shell 编程跟java、php编程一样,只要有一个能编写代码的文本编辑器和一个能解释执行的脚本解释器就可以了。
Linux的Shell种类众多,常见的有:
- Bourne Shell(/usr/bin/sh或/bin/sh)
- Bourne Again Shell(/bin/bash)
- C Shell(/usr/bin/csh)
- K Shell(/usr/bin/ksh)
- Shell for Root(/sbin/sh)
- ……
本教程关注的是 Bash,也就是 Bourne Again Shell,由于易用和免费,Bash在日常工作中被广泛使用。同时,Bash也是大多数Linux系统默认的Shell。
在一般情况下,人们并不区分 Bourne Shell 和 Bourne Again Shell,所以,像 #!/bin/sh,它同样也可以改为#!/bin/bash。
#!告诉系统其后路径所指定的程序即是解释此脚本文件的Shell程序。
第一个shell脚本
打开文本编辑器(可以使用vi/vim命令来创建文件),新建一个文件test.sh,扩展名为sh(sh代表shell),扩展名并不影响脚本执行,见名知意就好,如果你用php写shell 脚本,扩展名就用php好了。
输入一些代码,第一行一般是这样:|
1
2
|
#!/bin/bashecho"Hello World !" |
"#!" 是一个约定的标记,它告诉系统这个脚本需要什么解释器来执行,即使用哪一种Shell。
echo命令用于向窗口输出文本。
运行Shell脚本有两种方法:
1、作为可执行程序
将上面的代码保存为test.sh,并cd到相应目录:
|
1
2
|
chmod+x ./test.sh #使脚本具有执行权限./test.sh #执行脚本 |
注意,一定要写成./test.sh,而不是test.sh,运行其它二进制的程序也一样,直接写test.sh,linux系统会去PATH里寻找有没有叫test.sh的,而只有/bin, /sbin, /usr/bin,/usr/sbin等在PATH里,你的当前目录通常不在PATH里,所以写成test.sh是会找不到命令的,要用./test.sh告诉系统说,就在当前目录找。
2、作为解释器参数
这种运行方式是,直接运行解释器,其参数就是shell脚本的文件名,如:
|
1
2
|
/bin/shtest.sh/bin/phptest.php |
这种方式运行的脚本,不需要在第一行指定解释器信息,写了也没用。
定义变量时,变量名不加美元符号($,PHP语言中变量需要),如:
|
1
|
your_name="ziqiangxuetang.com" |
注意,变量名和等号之间不能有空格,这可能和你熟悉的所有编程语言都不一样。同时,变量名的命名须遵循如下规则:
- 首个字符必须为字母(a-z,A-Z)。
- 中间不能有空格,可以使用下划线(_)。
- 不能使用标点符号。
- 不能使用bash里的关键字(可用help命令查看保留关键字)。
除了显式地直接赋值,还可以用语句给变量赋值,如:
|
1
|
forfilein`ls/etc` |
以上语句将 /etc 下目录的文件名循环出来。
使用变量
使用一个定义过的变量,只要在变量名前面加美元符号即可,如:
|
1
2
3
|
your_name="qinjx"echo$your_nameecho${your_name} |
变量名外面的花括号是可选的,加不加都行,加花括号是为了帮助解释器识别变量的边界,比如下面这种情况:
|
1
2
3
|
forskill inAda Coffe Action Java doecho"I am good at ${skill}Script"done |
如果不给skill变量加花括号,写成echo "I am good at $skillScript",解释器就会把$skillScript当成一个变量(其值为空),代码执行结果就不是我们期望的样子了。
推荐给所有变量加上花括号,这是个好的编程习惯。
已定义的变量,可以被重新定义,如:
|
1
2
3
4
|
your_name="tom"echo$your_nameyour_name="alibaba"echo$your_name |
这样写是合法的,但注意,第二次赋值的时候不能写$your_name="alibaba",使用变量的时候才加美元符($)。
Shell 字符串
字符串是shell编程中最常用最有用的数据类型(除了数字和字符串,也没啥其它类型好用了),字符串可以用单引号,也可以用双引号,也可以不用引号。单双引号的区别跟PHP类似。
单引号
|
1
|
str='this is a string' |
单引号字符串的限制:
- 单引号里的任何字符都会原样输出,单引号字符串中的变量是无效的;
- 单引号字串中不能出现单引号(对单引号使用转义符后也不行)。
双引号
|
1
2
|
your_name='qinjx'str="Hello, I know your are \"$your_name\"! \n" |
双引号的优点:
- 双引号里可以有变量
- 双引号里可以出现转义字符
拼接字符串
|
1
2
3
4
|
your_name="qinjx"greeting="hello, "$your_name" !"greeting_1="hello, ${your_name} !"echo$greeting $greeting_1 |
获取字符串长度
|
1
2
|
string="abcd"echo${#string} #输出 4 |
提取子字符串
|
1
2
|
string="alibaba is a great company"echo${string:1:4} #输出liba |
查找子字符串
|
1
2
|
string="alibaba is a great company"echo`exprindex "$string"is` |
Shell 数组
bash支持一维数组(不支持多维数组),并且没有限定数组的大小。
类似与C语言,数组元素的下标由0开始编号。获取数组中的元素要利用下标,下标可以是整数或算术表达式,其值应大于或等于0。
定义数组
在Shell中,用括号来表示数组,数组元素用"空格"符号分割开。定义数组的一般形式为:
|
1
|
数组名=(值1 值2 ... 值n) |
例如:
|
1
|
array_name=(value0 value1 value2 value3) |
或者
|
1
2
3
4
5
6
|
array_name=(value0value1value2value3) |
还可以单独定义数组的各个分量:
|
1
2
3
|
array_name[0]=value0array_name[1]=value1array_name[n]=valuen |
可以不使用连续的下标,而且下标的范围没有限制。
读取数组
读取数组元素值的一般格式是:
|
1
|
${数组名[下标]} |
例如:
|
1
|
valuen=${array_name[n]} |
使用@符号可以获取数组中的所有元素,例如:
|
1
|
echo${array_name[@]} |
获取数组的长度
获取数组长度的方法与获取字符串长度的方法相同,例如:
|
1
2
3
4
5
6
|
# 取得数组元素的个数length=${#array_name[@]}# 或者length=${#array_name[*]}# 取得数组单个元素的长度lengthn=${#array_name[n]} |
Shell 注释
以"#"开头的行就是注释,会被解释器忽略。
sh里没有多行注释,只能每一行加一个#号。只能像这样:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
|
#--------------------------------------------# 这是一个自动打ipa的脚本,基于webfrogs的ipa-build书写:# https://github.com/webfrogs/xcode_shell/blob/master/ipa-build# 功能:自动为etao ios app打包,产出物为14个渠道的ipa包# 特色:全自动打包,不需要输入任何参数#--------------------------------------------##### 用户配置区 开始 ######## 项目根目录,推荐将此脚本放在项目的根目录,这里就不用改了# 应用名,确保和Xcode里Product下的target_name.app名字一致###### 用户配置区 结束 ##### |
如果在开发过程中,遇到大段的代码需要临时注释起来,过一会儿又取消注释,怎么办呢?
每一行加个#符号太费力了,可以把这一段要注释的代码用一对花括号括起来,定义成一个函数,没有地方调用这个函数,这块代码就不会执行,达到了和注释一样的效果。
Shell echo命令
Shell 的 echo 指令与 PHP 的 echo 指令类似,都是用于字符串的输出。命令格式:
|
1
|
echostring |
您可以使用echo实现更复杂的输出格式控制。
1.显示普通字符串:
|
1
|
echo"It is a test" |
这里的双引号完全可以省略,以下命令与上面实例效果一致:
|
1
|
echoIt is a test |
2.显示转义字符
|
1
|
echo"\"It is a test\"" |
结果将是:
|
1
|
"It is a test" |
同样,双引号也可以省略
3.显示变量
read 命令从标准输入中读取一行,并把输入行的每个字段的值指定给 shell 变量
|
1
2
3
|
#!/bin/shreadnameecho"$name It is a test" |
以上代码保存为 test.sh,name 接收标准输入的变量,结果将是:
|
1
2
3
|
[root@www ~]# sh test.shOK #标准输入OK It is a test#输出 |
4.显示换行
|
1
2
|
echo-e "OK!\n"# -e 开启转义echo"It it a test" |
输出结果:
|
1
2
3
|
OK!It it a test |
5.显示不换行
|
1
2
3
|
#!/bin/shecho-e "OK! \c"# -e 开启转义 \c 不换行echo"It is a test" |
输出结果:
|
1
|
OK! It is a test |
6.显示结果定向至文件
|
1
|
echo"It is a test"> myfile |
7.原样输出字符串,不进行转义或取变量(用单引号)
|
1
|
echo'$name\"' |
输出结果:
|
1
|
$name\" |
8.显示命令执行结果
|
1
|
echo`date` |
结果将显示当前日期
|
1
|
Thu Jul 24 10:08:46 CST 2014 |
Shell中的 test 命令用于检查某个条件是否成立,它可以进行数值、字符和文件三个方面的测试。
数值测试
| 参数 | 说明 |
|---|---|
| -eq | 等于则为真 |
| -ne | 不等于则为真 |
| -gt | 大于则为真 |
| -ge | 大于等于则为真 |
| -lt | 小于则为真 |
| -le | 小于等于则为真 |
实例演示:
|
1
2
3
4
5
6
7
8
|
num1=100num2=100iftest$[num1] -eq$[num2]thenecho'The two numbers are equal!'elseecho'The two numbers are not equal!'fi |
输出结果:
|
1
|
The two numbers are equal! |
字符串测试
| 参数 | 说明 |
|---|---|
| = | 等于则为真 |
| != | 不相等则为真 |
| -z 字符串 | 字符串长度伪则为真 |
| -n 字符串 | 字符串长度不伪则为真 |
实例演示:
|
1
2
3
4
5
6
7
8
|
num1=100num2=100iftestnum1=num2thenecho'The two strings are equal!'elseecho'The two strings are not equal!'fi |
输出结果:
|
1
|
The two strings are equal! |
文件测试
| 参数 | 说明 |
|---|---|
| -e 文件名 | 如果文件存在则为真 |
| -r 文件名 | 如果文件存在且可读则为真 |
| -w 文件名 | 如果文件存在且可写则为真 |
| -x 文件名 | 如果文件存在且可执行则为真 |
| -s 文件名 | 如果文件存在且至少有一个字符则为真 |
| -d 文件名 | 如果文件存在且为目录则为真 |
| -f 文件名 | 如果文件存在且为普通文件则为真 |
| -c 文件名 | 如果文件存在且为字符型特殊文件则为真 |
| -b 文件名 | 如果文件存在且为块特殊文件则为真 |
实例演示:
|
1
2
3
4
5
6
7
|
cd/biniftest-e ./bashthenecho'The file already exists!'elseecho'The file does not exists!'fi |
输出结果:
|
1
|
The filealready exists! |
另外,Shell还提供了与( ! )、或( -o )、非( -a )三个逻辑操作符用于将测试条件连接起来,其优先级为:"!"最高,"-a"次之,"-o"最低。例如:
|
1
2
3
4
5
6
7
|
cd/biniftest-e ./notFile-o ./bashthenecho'One file exists at least!'elseecho'Both dose not exists!'fi |
输出结果:
|
1
|
One fileexists at least! |
|
1
2
3
4
5
6
7
|
|
在sh/bash里可不能这么写,如果else分支没有语句执行,就不要写这个else,就像这样
if else
if
if 语句语法格式:
|
1
2
3
4
5
6
7
|
ifconditionthencommand1 command2...commandN fi |
写成一行(适用于终端命令提示符):
|
1
|
if`ps-ef | grepssh`; thenechohello; fi |
末尾的fi就是if倒过来拼写,后面还会遇到类似的。
if else
if else 语法格式:
|
1
2
3
4
5
6
7
8
9
|
ifconditionthencommand1 command2...commandNelsecommandfi |
if else-if else
if else-if else 语法格式:
|
1
2
3
4
5
6
7
8
|
ifcondition1thencommand1elifcondition2command2elsecommandNfi |
if else语句经常与test命令结合使用,如下所示:
|
1
2
3
4
5
6
7
8
|
num1=$[2*3]num2=$[1+5]iftest$[num1] -eq$[num2]thenecho'The two numbers are equal!'elseecho'The two numbers are not equal!'fi |
输出结果:
|
1
|
The two numbers are equal! |
for 循环
与其他编程语言类似,Shell支持for循环。
for循环一般格式为:
|
1
2
3
4
5
6
7
|
forvar initem1 item2 ... itemNdocommand1command2...commandNdone |
写成一行:
|
1
|
forvar initem1 item2 ... itemN; docommand1; command2… done; |
当变量值在列表里,for循环即执行一次所有命令,使用变量名获取列表中的当前取值。命令可为任何有效的shell命令和语句。in列表可以包含替换、字符串和文件名。
in列表是可选的,如果不用它,for循环使用命令行的位置参数。
例如,顺序输出当前列表中的数字:
|
1
2
3
4
|
forloop in1 2 3 4 5doecho"The value is: $loop"done |
输出结果:
|
1
2
3
4
5
|
The value is: 1The value is: 2The value is: 3The value is: 4The value is: 5 |
顺序输出字符串中的字符:
|
1
2
3
4
|
forstr in'This is a string'doecho$strdone |
输出结果:
|
1
|
This is a string |
while 语句
while循环用于不断执行一系列命令,也用于从输入文件中读取数据;命令通常为测试条件。其格式为:
|
1
2
3
4
|
whileconditiondocommanddone |
命令执行完毕,控制返回循环顶部,从头开始直至测试条件为假。
以下是一个基本的while循环,测试条件是:如果COUNTER小于5,那么条件返回真。COUNTER从0开始,每次循环处理时,COUNTER加1。运行上述脚本,返回数字1到5,然后终止。
|
1
2
3
4
5
6
|
COUNTER=0while[ $COUNTER -lt 5 ]doCOUNTER='expr $COUNTER+1'echo$COUNTERdone |
运行脚本,输出:
|
1
2
3
4
5
|
12345 |
while循环可用于读取键盘信息。下面的例子中,输入信息被设置为变量FILM,按 结束循环。
|
1
2
3
4
5
6
|
echo'type to terminate'echo-n 'enter your most liked film: 'whilereadFILMdoecho"Yeah! great film the $FILM"done |
运行脚本,输出类似下面:
|
1
2
3
|
type to terminateenter your most liked film: Sound of MusicYeah! great film the Sound of Music |
无限循环
无限循环语法格式:
|
1
2
3
4
|
while:docommanddone |
或者
|
1
2
3
4
|
whiletruedocommanddone |
或者
|
1
|
for(( ; ; )) |
until 循环
until循环执行一系列命令直至条件为真时停止。
until循环与while循环在处理方式上刚好相反。
一般while循环优于until循环,但在某些时候—也只是极少数情况下,until循环更加有用。
until 语法格式:
|
1
2
3
4
|
untilconditiondocommanddone |
条件可为任意测试条件,测试发生在循环末尾,因此循环至少执行一次—请注意这一点。
case
Shell case语句为多选择语句。可以用case语句匹配一个值与一个模式,如果匹配成功,执行相匹配的命令。case语句格式如下:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
case值 in模式1)command1command2...commandN;;模式2)command1command2...commandN;;esac |
case工作方式如上所示。取值后面必须为单词in,每一模式必须以右括号结束。取值可以为变量或常数。匹配发现取值符合某一模式后,其间所有命令开始执行直至 ;;。
取值将检测匹配的每一个模式。一旦模式匹配,则执行完匹配模式相应命令后不再继续其他模式。如果无一匹配模式,使用星号 * 捕获该值,再执行后面的命令。
下面的脚本提示输入1到4,与每一种模式进行匹配:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
echo'Input a number between 1 to 4'echo'Your number is:\c'readaNumcase$aNum in1) echo'You select 1';;2) echo'You select 2';;3) echo'You select 3';;4) echo'You select 4';;*) echo'You do not select a number between 1 to 4';;esac |
输入不同的内容,会有不同的结果,例如:
|
1
2
3
|
Input a number between 1 to 4Your number is:3You select3 |
跳出循环
在循环过程中,有时候需要在未达到循环结束条件时强制跳出循环,Shell使用两个命令来实现该功能:break和continue。
break命令
break命令允许跳出所有循环(终止执行后面的所有循环)。
下面的例子中,脚本进入死循环直至用户输入数字大于5。要跳出这个循环,返回到shell提示符下,需要使用break命令。
|
1
2
3
4
5
6
7
8
9
10
11
12
13
|
#!/bin/bashwhile:doecho-n "Input a number between 1 to 5: "readaNumcase$aNum in1|2|3|4|5) echo"Your number is $aNum!";;*) echo"You do not select a number between 1 to 5, game is over!"break;;esacdone |
continue
|
1
|
continue命令与break命令类似,只有一点差别,它不会跳出所有循环,仅仅跳出当前循环。 |
对上面的例子进行修改:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
#!/bin/bashwhile:doecho-n "Input a number between 1 to 5: "readaNumcase$aNum in1|2|3|4|5) echo"Your number is $aNum!";;*) echo"You do not select a number between 1 to 5!"continueecho"Game is over!";;esacdone |
运行代码发现,当输入大于5的数字时,该例中的循环不会结束,语句 echo "Game is over!" 永远不会被执行。
esac
case的语法和C family语言差别很大,它需要一个esac(就是case反过来)作为结束标记,每个case分支用右圆括号,用两个分号表示break。
linux shell 可以用户定义函数,然后在shell脚本中可以随便调用。
shell中函数的定义格式如下:
|
1
2
3
4
5
6
7
8
9
|
[ function] funname [()]{action;[returnint;]} |
说明:
-
1、可以带function fun() 定义,也可以直接fun() 定义,不带任何参数。
-
2、参数返回,可以显示加:return 返回,如果不加,将以最后一条命令运行结果,作为返回值。 return后跟数值n(0-255
下面的例子定义了一个函数并进行调用:
|
1
2
3
4
5
6
7
|
#!/bin/bashdemoFun(){echo"This is your first shell function!"}echo"Function begin..."demoFunecho"Function end!" |
输出结果:
|
1
2
3
|
Function begin...This is your first shell function!Function end! |
下面定义一个带有return语句的函数:
|
1
2
3
4
5
6
7
8
9
10
11
12
|
#!/bin/bashfunWithReturn(){echo"The function is to get the sum of two numbers..."echo-n "Input first number: "readaNumecho-n "Input another number: "readanotherNumecho"The two numbers are $aNum and $anotherNum !"return$(($aNum+$anotherNum))}funWithReturnecho"The sum of two numbers is $? !" |
输出类似下面:
|
1
2
3
4
5
|
The functionis to get the sumof two numbers...Input first number: 25Input another number: 50The two numbers are 25 and 50 !The sumof two numbers is 75 ! |
函数返回值在调用该函数后通过 $? 来获得。
注意:所有函数在使用前必须定义。这意味着必须将函数放在脚本开始部分,直至shell解释器首次发现它时,才可以使用。调用函数仅使用其函数名即可。
函数参数
在Shell中,调用函数时可以向其传递参数。在函数体内部,通过 $n 的形式来获取参数的值,例如,$1表示第一个参数,$2表示第二个参数...
带参数的函数示例:
|
1
2
3
4
5
6
7
8
9
10
11
|
#!/bin/bashfunWithParam(){echo"The value of the first parameter is $1 !"echo"The value of the second parameter is $2 !"echo"The value of the tenth parameter is $10 !"echo"The value of the tenth parameter is ${10} !"echo"The value of the eleventh parameter is ${11} !"echo"The amount of the parameters is $# !"echo"The string of the parameters is $* !"}funWithParam 1 2 3 4 5 6 7 8 9 34 73 |
输出结果:
|
1
2
3
4
5
6
7
|
The value of the first parameter is 1 !The value of the second parameter is 2 !The value of the tenth parameter is 10 !The value of the tenth parameter is 34 !The value of the eleventh parameter is 73 !The amount of the parameters is 12 !The string of the parameters is 1 2 3 4 5 6 7 8 9 34 73 !" |
注意,$10 不能获取第十个参数,获取第十个参数需要${10}。当n>=10时,需要使用${n}来获取参数。
另外,还有几个特殊字符用来处理参数:
| 参数处理 | 说明 |
|---|---|
| $# | 传递到脚本的参数个数 |
| $* | 以一个单字符串显示所有向脚本传递的参数 |
| $$ | 脚本运行的当前进程ID号 |
| $! | 后台运行的最后一个进程的ID号 |
| $@ | 与$#相同,但是使用时加引号,并在引号中返回每个参数。 |
| $- | 显示Shell使用的当前选项,与set命令功能相同。 |
| $? | 显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误。 |

浙公网安备 33010602011771号