工作中难免会使用一些shell脚本进行自动化管理,今天就简单的总结下shell的使用。
目录:
shell 的简单介绍
shell 脚本的一般格式
shell 脚本的执行
一个简单的例子
卷标与运算符号:declare
shell 的输入输出
逻辑判断与比较运算符
条件式判断 if
条件式判断 case
循环语句 for((;;))、for in、while、until
自定义函数
如何 debug shell
结语
shell 的简单介绍
部分摘自Linux Shell编程入门、 鸟哥的认识与学习 BASH 、 鸟哥的学习 shell scripts
从程序员的角度来看, Shell本身是一种用C语言编写的程序,从用户的角度来看,Shell是用户与Linux操作系统沟通的桥梁。用户既可以输入命令执行,又可以利用 Shell脚本编程,完成更加复杂的操作。在Linux GUI日益完善的今天,在系统管理等领域,Shell编程仍然起着不可忽视的作用。深入地了解和熟练地掌握Shell编程,是每一个Linux用户的必修功课之一。
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),等等。不同的Shell语言的语法有所不同,所以不能交换使用。每种Shell都有其特色之处,基本上,掌握其中任何一种 就足够了。在本文中,我们关注的重点是Bash,也就是Bourne Again Shell,由于易用和免费,Bash在日常工作中被广泛使用;同时,Bash也是大多数Linux系统默认的Shell。在一般情况下,人们并不区分 Bourne Shell和Bourne Again Shell,所以,在下面的文字中,我们可以看到#!/bin/sh,它同样也可以改为#!/bin/bash。
shell 脚本的一般格式
利用vi等文本编辑器编写Shell脚本的格式是固定的,如下:
#!/bin/sh
#comments
Your commands go here
首行中的符号#!告诉系统其后路径所指定的程序即是解释此脚本文件的Shell程序。如果首行没有这句话,在执行脚本文件的时候,将会出现错误。后续的部分就是主程序,Shell脚本像高级语言一样,也有变量赋值,也有控制语句。除第一行外,以#开头的行就是注释行,直到此行的结束。如果一行未完成,可以在行尾加上”,这个符号表明下一行与此行会合并为同一行。
shell 脚本的执行
编辑完毕,将脚本存盘为filename.sh,文件名后缀sh表明这是一个Bash脚本文件。执行脚本的时候,要先将脚本文件的属性改为可执行的:
chmod +x filename.sh
执行脚本的方法是:
使用 “sh 文件名” 也可以执行shell程序。
一个简单的例子
这个例子很简单,就是在屏幕上打印”Hello ! How are you ?”。
1 2 3 4 5 6 | #!/bin/bash <==在 # 之后加上 ! 与 shell 的名称,用来宣告使用的 shell # 这个脚本的用途在于列出 Hello ! How are you 在屏幕上 # 建檔日期: 2002/05/20 # Made by VBird hello=Hello\ \!\ How\ are\ you\ \? <==这就是变量啦! echo $hello |
下面这个例子,用来区别单引号与双引号的。这个跟php中的效果是一样的。
1 2 3 4 5 6 7 8 9 10 11 | [root @test test]# vi test02-2var.sh #!/bin/bash # 这个脚本用途在于引用两个变量,顺便比较一下 " 与 ' 的异同 # Date: 2002/06/27 # Made by VBird name="V.Bird" myname1="My name is $name" myname2='My name is $name' echo $name echo $myname1 echo $myname2 |
1 2 3 4 | [root @test test]# sh test02-2var.sh V.Bird My name is V.Bird My name is $name |
卷标与运算符号:declare
declare 这个用作声明变量的类型,跟SQL Server的存储过程类似。shell中变量的类型默认是字符型的,我们可以通过下面的例子看出。
1 2 3 4 5 | [root @test test]# a=3 [root @test test]# b=5 [root @test test]# c=$a*$b [root @test test]# echo $c 3*5 <==糟糕!怎么变成了字符串了?! |
declare的用法如下:
[test @test test]# declare [-afirx]
参数说明:
-a :定义为数组 array
-f :定义为函数 function
-i :定义为整数 integer
-r :定义为『只读』
-x :定义为透过环境输出变量
范例:
[test @test test]# declare -i a=3
[test @test test]# declare -i b=5
[test @test test]# declare -i c=$a*$b
[test @test test]# echo $c
15 <==变成数字啰! ^_^
shell 的输入输出
输出在前面就用到很多次了,就是echo $name
而输入的话,用的是read命令,它的作用跟c中的scanf类似,将用户输入保存到某个变量。
1 2 3 4 5 6 7 8 9 10 11 | [root @test test]# vi test04-read.sh #!/bin/bash # This program is used to "read" variables # VBird 2002/06/27 echo "Please keyin your name, and press Enter to start." read name echo "This is your keyin data ==> $name" [root @test test]# sh test04-read.sh Please keyin your name, and press Enter to start. VBird Tsai This is your keyin data ==> VBird Tsai |
而命令行参数的读入,是保存在$0等变量中的。
myscript opt1 opt2 opt3 opt4
$0 : myscript 亦即是 script 的檔名
$1 : opt1 亦即是第一个附加的参数 (parameter)
$2 : opt2
$3 : opt3
$$ : Shell本身的PID(ProcessID)
$! : Shell最后运行的后台Process的PID
$? : 最后运行的命令的结束代码(返回值)
$- : 使用Set命令设定的Flag一览
$* : 所有参数列表。如”$*”用「”」括起来的情况、以”$1 $2 … $n”的形式输出所有参数。
$@ : 所有参数列表。如”$@”用「”」括起来的情况、以”$1″ “$2” … “$n” 的形式输出所有参数。
$# : 添加到Shell的参数个数
$0 : Shell本身的文件名
$1~$n : 添加到Shell的各参数值。$1是第1参数、$2是第2参数…。
逻辑判断与比较运算符
shell中逻辑表达式多而复杂,分为很多种类型,下面一一列举。
逻辑判断式一:关于档案与目录的侦测逻辑卷标!
-f 常用!侦测『档案』是否存在
-d 常用!侦测『目录』是否存在
-b 侦测是否为一个『 block 档案』
-c 侦测是否为一个『 character 档案』
-S 侦测是否为一个『 socket 标签档案』
-L 侦测是否为一个『 symbolic link 的档案』
-e 侦测『某个东西』是否存在!
逻辑判断式二:关于程序的逻辑卷标!
-G 侦测是否由 GID 所执行的程序所拥有
-O 侦测是否由 UID 所执行的程序所拥有
-p 侦测是否为程序间传送信息的 name pipe 或是 FIFO
逻辑判断式三:关于档案的属性侦测!
-r 侦测是否为可读的属性
-w 侦测是否为可以写入的属性
-x 侦测是否为可执行的属性
-s 侦测是否为『非空白档案』
-u 侦测是否具有『 SUID 』的属性
-g 侦测是否具有『 SGID 』的属性
-k 侦测是否具有『 sticky bit 』的属性
逻辑判断式四:两个档案之间的判断与比较 ;例如『 test file1 -nt file2 』
-nt 第一个档案比第二个档案新
-ot 第一个档案比第二个档案旧
-ef 第一个档案与第二个档案为同一个档案( link 之类的档案)
逻辑判断式五:逻辑的『与(and)』『或(or)』!
&& 逻辑的 AND 的意思
|| 逻辑的 OR 的意思
比较运算符
= 等于
!= 不等于
< 小于 > 大于
-eq 等于
-ne 不等于
-lt 小于
-gt 大于
-le 小于或等于
-ge 大于或等于
-a 双方都成立(and)
-o 单方成立(or)
-z 空字符串
-n 非空字符串
条件式判断 if
if主要有两种,一种只有一个条件判断,另一种是有多个条件判断。
一:if then fi 的方式 二:if … then …. else if …. then … end if
下面举个例子:
1 2 3 4 5 6 7 | if [ 条件判断一 ] && (||) [ 条件判断二 ]; then <== if 是起始的意思,后面可以接若干个判断式,使用 && 或 || 执行内容程序 elif [ 条件判断三 ] && (||) [ 条件判断四 ]; then <==第二段的判断,如果第一段没有符合就来此搜寻条件 执行第二段内容程序 else <==当前两段都不符合时,就以这段内容来执行! 执行第三段内容程序 fi <==结束 if then 的条件判断! |
不过,这里有几个新手常犯的错误,我们需要来加强说明一下:
在 [ ] 当中,只能有一个判别式;
在 [ ] 与 [ ] 当中,可以使用 && 或 || 来组织判别式;
每一个独立的组件之间『都需要有空格键来隔开』!
尤其是最后一点,最容易犯的错啦!好了,我们来进行一个简单的判别式好了!
条件式判断 case
shell中的case跟c中的switch-case类似。
1 2 3 4 5 6 7 8 9 10 11 | case 种类方式(string) in <==开始阶段,那个种类方式可分成两种类型,通常使用 $1 这一种直接下达类型! 种类方式一) 程序执行段 ;; <==种类方式一的结束符号! 种类方式二) 程序执行段 ;; *) echo "Usage: {种类方式一|种类方式二}" <==列出可以利用的参数值! exit 1 esac <==这个 case 的设定结束处! |
下面是一个具体的例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | [root @test test]# vi test10-case.sh #!/bin/bash # program: Using case mode # Made by: VBird # date: 2002/06/27 # content: I will use this program to study the case mode! # 1. print this program echo "Press your select one, two, three" read number case $number in one) echo "your choice is one" ;; two) echo "your choice is two" ;; three) echo "your choice is three" ;; *) echo "Usage {one|two|three}" exit 1 esac |
1 2 3 4 | [root @test test]# sh test10-case.sh Press your select one, two, three two <=这一行是您输入的呦! your choice is two |
循环语句 for((;;))、for in、while、until
这几种循环的意思跟其他语言是一致的,就不再解释了。只需要明白其写法就OK了,直接上例子。
for((;;))
1 2 3 4 5 6 | [test @test test]# vi test11-loop.sh #!/bin/bash # Using for and loop # VBird 2002/06/27 declare -i s # <==变量宣告 for (( i=1; i<=100; i=i+1 )) do s=s+i done echo "The count is ==> $s" |
1 2 3 4 | [test @test test]# sh test11-loop.sh The count is ==> 5050 while |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | [test @test test]# vi test12-loop.sh #!/bin/bash # Using while and loop # VBird 2002/06/27 declare -i i declare -i s while [ "$i" != "101" ] do s=s+i i=i+1 done echo "The count is ==> $s" until |
1 2 3 4 5 6 7 8 9 10 11 12 | [test @test test]# vi test13-loop.sh #!/bin/bash # Using until and loop # VBird 2002/06/27 declare -i i declare -i s until [ "$i" = "101" ] do s=s+i i=i+1 done echo "The count is ==> $s" |
1 2 3 4 | [test @test test]# sh test12-loop.sh The count is ==> 5050 for in |
1 2 3 4 5 6 7 8 9 10 11 | [test @test test]# vi test14-for.sh #!/bin/bash # using for...do ....done # VBird 2002/06/27 LIST="Tomy Jony Mary Geoge" for i in $LIST do echo $i done |
1 2 3 4 5 | [test @test test]# sh test5.sh Tomy Jony Mary Geoge |
上面的 $LIST 这个变量当中,以空格键来分隔的时候,共可以分离出来四个!所以当以 do ….. done … 就可以分别打印四个变量。
for in的类型较多,这里贴一篇讲解for in的文章, 总结下面大概有这么几种:
用空格分开的单个字符串
字符串数组-如 for i in a b c
字符串数组-利用“或$()合成的,如for i in $(ls *.txt)
自定义函数
自定义函数主要有以下几个方面:
函数定义的格式
函数相关变量作用域
函数的作用域
有参考这篇文章
函数的定义如下:
1 2 3 4 5 | [ function ] funname [()] { action; [return int;] } |
说明:
可以带function fun() 定义,也可以直接fun() 定义,不带任何参数。
参数返回,可以显示加:return 返回,如果不加,将以最后一条命令运行结果,作为返回值。 return后跟数值n(0-255)
必须在调用函数地方之前,声明函数,shell脚本是逐行运行。不会像其它语言一样先预编译。一次必须在使用函数前先声明函数。
shell中函数看作新的命令,因此各个输入参数直接用空格分隔。参数可以通过:$0…$n得到。 $0代表函数本身。
函数返回值,只能通过$? 系统变量获得。直接通过=,获得是空值。
函数相关变量作用域:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | #!/bin/sh declare num=1000; uname() { echo "test!"; ((num++)); return 100; } testvar() { local num=10; ((num++)); echo $num; } uname; echo $? echo $num; testvar; echo $num; sh testfun2.sh test! 100 1001 11 1001 |
从上面可以看出:
在函数外面申请的变量是全局变量
在函数内用local申明的变量是局部变量
函数的作用域:
还有个例子就不举了,函数在申明之后才能使用。在申明以前调用会报未定义的错误。
如何 debug shell
scripts 在执行之前,最怕的就是出现问题了!那么我们如何 debug 呢?有没有办法不需要透过直接执行该 scripts 就可以来判断是否有问题呢!?呵呵!当然是有的!我们就直接以 sh 来进行判断吧!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | [test @test test]# sh [-nvx] scripts -n :不要执行 scripts ,查询 scripts 内的语法,若有错误则予以列出! -v :在执行 scripts 之前,先将 scripts 的内容显示在屏幕上; -x :将有使用到的 scripts 内容显示在屏幕上,与 -v 稍微不同! [test @test test]# sh -n test01-hello.sh [test @test test]# sh -v test01-hello.sh #!/bin/bash # This program will print the "Hello! How are you" in your monitor # Date: 2002/06/27 # User: VBird hello="Hello! How are you" echo $hello Hello! How are you [test @test test]# sh -x test01-hello.sh + hello=Hello! How are you + echo 'Hello!' How are you Hello! How are you |
对于 Shell scripts 的学习方法上面,需要『多看、多模仿、并加以修改成自己的样式!』是最快的学习手段了!网络上有相当多的朋友在开发一些相当有用的 scripts ,若是您可以将对方的 scripts 拿来,并且改成适合自己主机的样子!那么学习的效果会是最快的呢!
结语
shell在当今窗口程序盛行的时代,还是占有一席之地。就我作为一个PHPer,用到shell的场景并不是很多,如果单单作为开发,把运维的工作也包了,这个我也是醉了。对于某些提高工作效率的工具,能够使用自己更得心应手的语言实现,就没有用shell的必要了。比如文本分析,我就个PHPer,肯定不会去学英汉字典厚的AWK。
http://yaoguais.com/article/linux/shell.html