前言
个人感觉这一部分,应该是相当重要的一部分了,前面很多地方,如果我们记得不是很清楚还可以通过查看文档来解决,但是这一部分,可以说是脚本的基础了,这一部分要是不会,那。。。麻烦了。。。本来学习linux目的是为了提高自己的竞争力,结果人问你一句怎么遍历一个数组,你回答不上来,是不是很尴尬。。。
作为一个程序,别的不说,你至少得有个逻辑判断,能够实现根据用户的不同选择,做出不同的动作,从而实现不同的结果。
这篇blog主要说的就是这么一个功能。
简单的逻辑判断表达式
其实我们之前学了好几个逻辑判断表达式了,比如&&和||这种连接多个命令的判断,还有${变量:-默认值}这种为控制赋值的表达式,还有awk命令中的表达式,等等。
这里再学一个比较好玩的逻辑判断,test命令。
test命令可以用来检验文件的类型以及比对值。
比如,我们之前进行创建文件的时候通常会使用这种命令来执行。
现在我们出现了一种替代的方式,我们可以通过ls 文件名|| touch 文件名
这种方式来判断文件是否存在,这里需要注意的是test -e 这种方式并不会输出具体的内容,但是我们可以通过他的回传值来做处理,比如:test -e 文件名
貌似看起来更复杂了一点,但是我们可以配合待会学到的条件判断语句来使用,但是在这之前,我们还是要先学会应该怎么用test命令。test -e 文件名||touch 文件名
test判断文件相关选项
其实这些选项还是比较容易记忆的,大多数都是一些单词的缩写。
-e 判断文件是否存在(exist) -f 判断文件是否存在且是一个文件(file) -d 判断文件是否存在且是一个目录(direct) -b 文件存在且是一个块装置文件(block) -c 文件存在且是一个字符装置文件(character) -S 文件存在且是一个Socket文件(Sockte) -P 文件存在且是一个Pipe文件(Pipe) -L 文件存在且是一个符号链接文件(Link) test判断文件权限相关
-r 文件存在且当前用户具有读权限(read) -w 文件存在且当前用户具有写权限(write) -x 文件存在且当前用户具有执行权限(execute) -u 文件存在且具有SUID权限 -g 文件存在且具有SGID权限 -k 文件存在且具有Sticky bit权限 -s 文件存在且改文件为空白文件 test判断两个文件的关系
命令格式 test 文件1 【选项】 文件2
-nt 判断文件1是否比文件2新(new than) -ot 判断文件1是否比文件2旧(old than) -ef 判断文件1和文件2是否指向同一个inode节点(我猜是不是equals file的缩写) test判断两个数值的大小
命令格式 test 数字 【选项】数字
-eq 两个数值相等(equals) -ne 两个数值不相等(not equals) -gt 第一个数大于第二个数(greater than) -lt 第一个数小于第二个数(less than) -ge 第一个数大于或等于第二个数(greater than or equals) -le 第一个数小于或等于第二个数(less than or equals) test判断两个字符串的关系
命令格式test 字符串 【选项】字符串 PS:需要注意的是,空字符串表示为0.
-z 字符串是否为0?(zero) -n 字符串是否非0?(not zero) == 两个字符串是否一致? != 两个字符串是否不一致?
test的多重条件判断--逻辑判断
-a 表示和,-a两边的表达式同时返回0,才返回0(and) -o 表示或者,-o两边的表达式有一个返回0就返回0(or) ! 表示非,不。将!后的表达式结果去相反的结果,比如之前为0,加上!,则表示不为0. 看起来test选项挺多的样子,其实如果真的静下心来敲一遍的话,你会惊讶的发现,貌似规律太明显,为什么辣么好记。。。当然,我相信,过不了几天我就会又忘了。但是没关系,至少我有个大致的印象了,以后用多了自然而然就记住了。
号外号外....在linux中还可以使用[]来代替test哟,比如我们可以通过 [ 5 -gt 3 ]来代替test 5 -gt 3 ,需要注意的是在中括号内每一个元件都要使用空格分开,包括元件和中括号之间.脚本的默认变量我们已经学习了可以和用户交互的命令read,但是通过read有一个缺点,用户执行完命令后,还要再根据提示输入所需的参数,那么我们可不可以实现类似我们之前使用的ls -l这种将参数直接跟在命令后面的形式呢?
和其他编程语言不同,这种跟在指令后面的参数,在脚本里,我们不需要自己定义,我们可以通过$n的形式来获取第n个参数.
比如:
注意看上图,var.sh的第二行和第三行,这里我们通过$0的形式输出了指令的名称,使用$1来输出我们第一个变量(nihao),一次类推,$2表示第2个参数...
除了$数字的形式,还有三个比较特殊的变量.
$#.通过$#可以获取当前命令后面跟的参数的个数.
$@表示获取所有的参数.
$*也表示获取素有的参数,和$@展示形式不一样.
写个脚本看一下.
然后测试一下:
默认变量的偏移
在脚本厉害还有一个挺好玩的东西,也是关于默认变量的,我们可以通过shift n 的形式删除掉前面n个变量,如果只写shift不写数字的话,表示删除一个变量.额,做个试验,说起来感觉怪怪的.![]()
看下执行结果.
看画红框的地方,是不是第一个变量没了,看起来挺好玩的,但是我还没有想到应用场景.
条件判断语句ifthen
终于到最好玩的地方了,记录这篇blog的时候,我老是想要跳过前面的内容,直接写这个位置,毕竟前面的太枯燥了.简单的条件判断语句,主要记住两个关键词if和then.具体怎么用呢.if[条件判断式]; then 怎么做 fi.这里的fi是if反过来,表示结束这个if.看个例子![]()
当然是用原生的test也是可以的.![]()
如果使用中括号的形式,可以使用&&代替-a选项,使用||代替-o 选项.比如:![]()
用什么样的方式,按照个人习惯吧,我个人还是比较喜欢中括号的形式,和以前学过的编程相似度高一点.下面写一个小例子玩一下,还是之前的计算器吧,![]()
我试了一下这个小例子,发现一个问题,就是乘法的时候,传递的不是*号本身,还是当前目录所有文件名,这个很尴尬,最前面的#@目的是为了打印出参数.可以通过\* 来使用功能,获取结果,当然也可以修改乘法的符号,比如换成字母X.这样一个简单的小计算器就实现了,不过这么多判断,好多if,是不是有点过分了,而且,在判断使用的计算方法时,这四个方法他们是不可能同时存在的,我们完全可以提出来,这样,就用到了if...then...elif...else....fi多重条件判断,看起来稍微复杂了,其实和之前还是一样的,没有多难.多重条件判断
多重条件判断如果用俗语说的话,就是,当怎样怎样的时候,我们就怎么做,或者当怎样怎样的时候,我们这么做,不然,我们就怎么做.个人感觉应该还算是比较简单的.下面搞一个最简单的练练手.![]()
目测看起来,也没有什么难的.下面试一下书中的例子,通过netstat来判断当前主机启动的服务.netstat命令用来显示网络链接,路由表,接口状态,伪装链接,网络链路信息和组播成员组,听起来非常高大上,接下来我们会使用到他的t,u,n,l选项,所以这里先看一下这几个选项的作用.t选项表示显示tcp链接,u选项表示显示udp链接,n表示显示IP地址,而不是主机名称.l表示显示正在监听的服务.先不使用书里的脚本,按照自己的思路来一份.先看看本机启动的服务.![]()
先看一下前两行数据,其实有点类似于标题和列名,所以对于我们分析数据来说是用不到的.然后我们主要是看一下有没有启动22SSH服务,和80网络服务,所以我们要对这些数据剔除第二行然后使用空格分割取第三部分,然后判断里面的内容是否有22端口或者80端口.然后打印出数据.netstat -utnl|awk '{if (NR>2){print $4}}'|awk -F ':' '{ print $2}'|grep ^[1-9]
写一个shell判断是否启用了服务.![]()
执行下,看看效果:![]()
ok,通过这个shell基本就了解了用法.这一块是条件语句的学习,条件语句还有一种类型.case in.分支语句
先看一个比较简单的shell.然后在理解case in.![]()
上图这是一个比较简单的case...in的示例,先说一下他的作用,在我们日常生活中case通常表示一种状况.case...in..语句的含义就是,当..处于某一种状态的情况下.要怎么去做.这个命令的格式是这样的:case 变量 in变量内容);;esac看第一行,这里的变量其实就是我们用来比较多数据,就像上图中的$1,我们主要是处理这个变量的各种状况.in 表示后面跟的是状态,比如上图的nihao,注意nihao后面还有一个),这个反括号是不能被省略的哟.而在反括号后面就可以跟处理这种状况的语句了,当出现;;的时候表示这种状况结束,注意这里是两个分号.这样,我们尝试分析一下上图中,表示当$1这个变量的值是nihao的时候,我们就打印出依据hello.比较容易理解吧.这样,下面我们修改一下之前的计算器,实际使用case in 操作一下.![]()
注意看上图中倒数第五行,那个*表示默认,如果上面的条件都不满足的话,就会走这个默认的。效果没啥意思,就不演示了。函数式编程
不知道有木有学过javaScript,或者其他编程语言,在js中我们可以通过
function 方法名(参数列表){
函数内容
}
这种方式来定义一些方法块,在需要使用的时候只要通过方法名(参数)这种形式就可以调用定义的这个方法了,在linux中也支持这种函数式的编程。
他的格式类似于js,
function 方法名(){
操作
}
需要注意的是,linux的函数式编程并没有参数列表,我们在使用参数的时候可以通过$n这种方式来获取第n个参数。
下面 我们改写一下上文中的计算器,使用函数式编程。
看一下之前的代码,我们可以提取出校验数字内容的方法和casein方法。
不过还有点需要注意的地方是,我们自定义的函数必须卸载最前面。这点和js是不同的!
ok,截止到目前为止,还都是相对比较简单的。虽然接下来的也很简单.
循环
在编程中,肯定不可能只有条件分支语句,还要有循环语句.循环语句的作用就是在满足某些条件下一直重复的做一些事.
在我们linux脚本中,有三种常用的循环方式,分别是:
1.while do done
2.until do done
3.for do done
分别看一下这三种循环的用法.
while循环
先看一个小例子.
while循环好像没啥好说的,只要说一下他的结构就好了.
while [条件]
do
做一些操作
done.
用于,当满足某一些条件的时候,就进行一些操作.
unitl循环
unitl [条件]do
做一些操作done.用于,当满足条件的时候才停止循环.不然就一直执行动作.
看这个两个循环,他们有一个共同的特点,就是他们循环的次数是不确定的,可能在第一次循环的之后,他就停了,也有可能在一万次循环之后,他还健在.我们讲这种循环成为不定循环.当然除了不定循环,我们还有次数固定的循环方式.
for循环
对于for循环有两种方式.第一种方式是:for 变量 in 集合do 做一些操作done做一个简单的小例子.
貌似也挺简单的.
另一种形式.
要注意上面的是双括号...
这种形式的结构是这样滴:
for((初始值;限制条件;执行步阶))
do
一些操作
done
可以看着上图的实例来理解.
到这的话,我们就学完了条件判断和循环.接下来用这两个做一个小训练,巩固一下.
画个菱形
分析
![]()
看上图这个菱形,这个是我们想要得到的结果,看看他有什么规律![]()
看上图可以发现将菱形分为两部分,一部分是正三角,一部分是倒三角.下面根据这个规律画一下.![]()
![]()
上面就是绘制菱形的代码了。再贴一份代码到这基本就学完了shell的基本编写。#!/bin/bash #定义一个绘制的函数 function draw(){ #无参数则跳出 test -z $1&&exit 1; #判断绘制正三角还是倒三角 line=1; #控制打印的次数,即菱形的高度 while [ "$line" -le "$1" ] #!/bin/bash #定义一个绘制的函数 function draw(){ #无参数则跳出 test -z $1&&exit 1; #判断绘制正三角还是倒三角 line=1; #控制打印的次数,即菱形的高度 while [ "$line" -le "$1" ] do content=""; #计算出菱形中间的位置(所处行数) let mid=$1/2+1; if [ "$line" -le "$mid" ]; then #先拼接空格,计算出空格数量 let blank_number=$1+1-2*$line; for((b=0;b<$blank_number;b++)) do content=$content" "; done #拼接*号,计算出*号的个数 let start_number=2*$line-1; for ((s=0;s<$start_number;s++)) do content=$content"* "; done #绘制倒三角 else #计算空格数量 let "blank_Number=2*($line-($1/2+1))"; #拼接空格 for((b=0;b<$blank_Number;b++)) do content=$content" "; done #计算*号的数量 let start_Number=2*$1-2*$line+1; #拼接*号 for((s=0;s<$start_Number;s++)) do content=$content"* "; done fi #输出这一行的内容 echo -e "$content"; let line++; done } #调用绘制菱形方法 declare -i height; test -z $1&&height=5||height=$1; #因为上面方法的规律是针对奇数高度的菱形,所以如果用户输入的是偶数,还要在处理一下。 [ $(($height%2)) -eq 0 ] && height=$(($height+1)); draw $height;
因为写这篇blog的时候,项目已经启动了,所以前前后后用了一个多周才写完这篇blog。sh脚本的debug
sh命令好像还很简单的,之前想专门写一遍blog记录一下的,最后想了想就那么点东西的话,还是放在这儿吧。sh -n 脚本,编译指定脚本,检查语法错误。sh -v 在执行脚本前,将脚本的内容输出出来。sh -x 执行脚本,并输出脚本的执行过程。虾米?看不懂?试一下就知道了。。。。so easy。。。