Linux之Shell脚本自动化编程六(shell运算符和流程控制)

版权声明: https://blog.csdn.net/qq_41740705/article/details/81813138

Shell 运算符

 

这个expr需要注意的是中间是必须有空格的,以前的文章里演示过。

 

注意*需要用反斜杠转义一下,这个以前也提到过。

 

 

对比一下上面,可以得出一个结论,一个就是echo $[]和[];echo $?的值正好是反的,这个我们以前也说过是正负逻辑的问题。第二就是在[]里比较数字的时候一定要加上$,不然其实比较的是字符串的ascii码,[ a == b ]这个是false,所以返回值是1,反之就是true,返回值是0。

 

在[];echo $?这种方式中,只写<>会被看作是重定向,所以会出现b是一个目录的错误,我们只能重定向到文件中,(虽然目录也是一种文件),我们可以加一个\就可以作为大于小于号。=赋值号在[];echo $?这种方法没办法执行,因为这里面其实这个等号是字符串的比较符号,下面会看到,还不如直接去掉[],在echo  $[]里面是有用的。

 

 

 

上面!false这种用法是错误的,有点想当然了,false是一个命令,echo $[]里面是这样的,如果这个变量没有被赋值,就打印0,如果里面再有一个!就打印1,这里面true和false同样被看作变量,所以echo $[!false]和echo $[!true]都打印1。

 

 

而[];echo $?了这里面如果不加$,就只是把akl看作是一个字符串,返回值一直是0,加一个!就是1,加了$和前面的正好相反,如果变量没有被赋值,返回的是1,被赋值了返回0,里面可以加!起到not也就是非的作用。

 

前面也说到过&&和||可以在双中括号里用,用单对中括号会报错提示少中括号,加\转义也不管用,但是其实双中括号中的&&和||我们完全可以用-a和-o代替。这里要稍微说一下为什么[ ! $a -lt 100 && $b -gt 100 ]];echo $?;[[ $a -lt 100 && ! $b -gt 100 ]];echo $?的结果不一样,这里其实是因为!的优先级高于&&,所以[ ! $a -lt 100 && $b -gt 100 ]];echo $?会把! $a -lt 100放在一起判定而不是! ($a -lt 100 && $b -gt 100)(这是先$a -lt 100 && $b -gt 100,最后非),a是10,是小于100的,但是再非一下,就是false,因为这是和,所以逻辑短路成立,后面也不用判定了,结果就是false,不过由于负逻辑,打印出来的是1。而[[ $a -lt 100 && ! $b -gt 100 ]];echo ?,前面是true,a=10<100,然后判定! $b -gt 100,因为b=20,还有一个非,所以返回是true,那么最后的返回是true,值是0。

 

=和==都可以用来判断字符串是否相等,[ a = a ]或者[ a == a ]的意思就是a这个字符串等于a,那当然是等于的,所以是true,0。如果是[ $a =a ]的意思是a变量是不是等于a字符串显然是不等于的,所以是false,也就是1。echo $[a==a]的值一直是1,这个前面也解释过很多次了。

[ a == 666 ]的结果为什么是0,大家也应该知道了。

 

值得说明的是,其实-n和string的效果是一样的,变量没有赋值的话长度就是0,空格在里面也是算字符的,如果用expr length计算长度,字符串里面有空格的话需要加上引号。

 

 

 

还是要提醒一点,true是0,而false是1,绿色的文件是可以执行的。

test

 

 

 

 

前面也提到过,[]和test的用法是一样的,当然,这个[]是[];echo $?这种方式。

Shell 流程控制

if

 

 

then和fi(结束符)都是不可以缺少的,再写脚本的时候分行写分号可以不要。写成一行的时候,if后面,then 命令后面都要加上分号的。if后面是按照真假来判断的,看到了是true就执行then后面的语句,不是按照0或者1来评判的,和正负逻辑无关,只看true还是false。另外:可以作为占位符写在cmd的位置,这样是不会报错的,如果什么都不写会报错,这个其实以前也提到过。

 

 

连if后面的条件判断也可以用:来当占位符。

 

显然是[ $a - lt $b]成立,它的结果为true,至于返回值是多少我们不关心。双小括号也可以代替中括号,并且不需要很烦人的空格,还有$好也不需要加,这个双小括号在上一讲中也介绍过。

 

当然也可以用test。

 

这段可以看看https://www.bilibili.com/video/av8104450/?p=3&t=168。

然后我们来稍微做一个简单的案例,真的很简单,就是成绩判定。输入一个成绩,然后判定是优秀,及格,还是不及格。

 

其中有一个是用[]是因为双小括号有一个奇怪的现象,就是输入数字,a==0判定居然是true的。

 

这样的输出是有问题的。

 

也可以把分数作为脚本的一个参数。

 

https://www.bilibili.com/video/av8104450/?p=4这个视频里面是mysql备份的一个例子,其实很简单的,它写的脚本也就是判断一下是不是root用户,如果不是就直接退出了子shell,因为脚本是在子shell里面执行的嘛。然后再判断要被备份到的文件夹是不是存在,不存在创建一个-p是递归创建,最后备份就可以了,备份的命令也就是mysqldump,不过我们还没有学到。

 

备份成功,也就是$?=0的话就打印备份成功,否则打印失败,请检查。

 

思路其实很简单,提醒一点,好孩子不要学里面的英语,很多基本语法都是错的,这个老师的英语水平连初中生都不如。如何实现自动备份,用计划任务就可以了,就是前面见过的crontab -e。

https://www.bilibili.com/video/av8104450/?p=5这个视频里面是写一个一键安装LAMP的脚本,视频里只用源码包安装,有点麻烦,我们还是用yum去安装更快。

 

 

安装apache是没有问题的,不过我不想看这些中间过程,那么就加一个重定向就行了。

 

 

 

之后的结果。

 

3这里忘记把Mysql改成PHP了,如果想做网站架构,这些可能还不够,可能还需要php-mysql等软件包,这里是为了写脚本而已,不是讲做网站,所以不展开了。你想看源码包安装的,还有配置lamp的,可以去看一看视频,源码包安装的优势在于软件的关联关系可以自己去设置,这以前也都讲过。

for

 

 

do和done是必不可少的,如果是写在一行的话,for in 的后面和do command...的后面都要加分号或者说do和done的前面要加分号。除了空格分开参数,用大括号这种集合也是可以的,小括号这种数组是不行的。不知道中括号为什么总是打印1,2。

 

 

中括号把我看懵了,我们还是用{},上面我们还看到了$*和$@的区别,$*是打印出来在一行的,$@是打印了三行,$*和"1  2 3"是一样的,因为空格在双引号里面,所以不分隔符的含义没有了,只是普通的空格,这样,for 循环只循环了一次。而$@相当于空格在引号外面,就是分隔符,“1” “2” “3”就是三个参数,for循环也是循环了三次。

 

所以下面的理解也是不对的,这个只输出了一次,是一次性输出,和直接echo 没有区别。

 

这个作者可能把in和python里面的搞混了,在bash的for这里,每次取的是一个参数或者说变量,而python里面的for in只是每次从列表,元祖中按照索引增加的顺序取元素,而不是变量,就像上面的a=(1,2,3);for i in $a;do echo $i;done,并不是把数组中的元素一个一个打出来,而是整个连逗号一起打出来了,而用大括号逗号隔开可以让其中的元素一个一个打出来,这是bash里除了空格以外的第二种方式。不过{}里面也别有空格,不然就会作为分隔符了,不过可以用\转义。还有一个小知识,python最后exit返回的参数可以用echo $?打出来。

 

当然如果仅仅是打印一串等差数列的话,用seq就可以做到,可以有三个参数,第一个是其实值,第二个是步长,默认是1,第三个终止的值。

 

 

最后还有一个10,大小问题没有截上去。参数必须都是浮点数,整数可以看作小数点后都为0的浮点数。

 

还可以看出如果步长为正,最后会停在小于等于终止数的数,反之停在大于等于的数。

如果步长为0,就一直打印起始值了。

 

再来看一个案例:

 

这个案例也不难,前面括号的时候就说过,双小括号里面可以写c语言的代码。这种方式可能可行,为什么说可能呢,因为好长时间我都没看到结果,我们完全可以不用双小括号强行用c语言,用{1..100}真的是秒出结果。

 

双小括号迟迟不出结果可能是因为在for后面,我多加了空格。

 

https://www.bilibili.com/video/av8104450/?p=7我只能说对这个视频很失望。

老师可不可以动点脑子,这里我介绍一种压缩打包后缀为log文件的方法,其实真的很简单,根本不用for,用for也完不成。

 

我们再解压看看是不是呢,压缩解压把文件树的结构也保留了,但是已经验证了是可以的,就这么简单,视频里的老师不仅英语差,脑子还有问题。

 

批量解压倒是需要一下for,因为tar命令每次只能解压解包一个压缩打包文件。

 

tar xzf的第二个参数应该是1.tar.gz的其中一个文件名,所以会出现2.tar.gz在压缩包里面没有找到。

while

 

 

let "int++"或者((int++))都可以。双小括号里面可以不要$。

 

也可以用[],不过这时候就需要$了。

 

上面的例子按ctrl+d确实可以退出,不过里面并没有if或者什么去判断,回想以前cat >1的时候,就是ctrl+D结束输入的,这个应该是bash的一个特性。其实ctrl+d还是有点特殊的,Ctrl+d有三个功能:1:终止输入,2:退出 shell,3:删除光标所在处字符。1用在有输入的时候,比如cat >和下面的read FILM,这都是要求有输入的。2和3以前区别过,如果shell里这一行不是空行,就是3,否则就是2。

 

echo -n的意思是不换行而已,默认都是换行的。

 

不过其实这里用的read和ctrl+d还是有点特殊的。如果只是read a这一条的话,按回车就可以结束输入了,不过这只是一次,其实在while里面也是,不过回车一次是结束这次循环的read输入,而ctrl+d是结束整个while的输入。

 

https://www.bilibili.com/video/av8104450/?p=7

这个老师虽然很水,但是上面的视频还是值得一看的。里面我觉得比较有用的就是依次读取文件的每一行,这个用python太好实现了,不过现在在bash里面看一看如何实现。下面是while和for如何实现。

 

for实现大家应该不会有什么疑问,不过while这个重定向为什么要加在done后面呢?

 

放在while后面每次都只读到了第一行,放在do和done里面,也不对,也只有放在done后面,我理解为是对整个while语句结构的重定向,只有这样才是一行一行读。

untill

 

 

这个感觉没什么好说的。

无限循环

 

除了上面这些以外,还有until false;do cmd;done这种也可以。下面是演示:

 

case

 

 

这里面的星号是任意多个任意字符,那么为什么这么写结果是对的呢?

 

是因为case其实是一个一个选择去匹配的,按照从上到下的顺序,那么如果我们把*这种情况提到3,4之前,那么输入3的时候,就会被*拦下,而*放在最后面只能匹配到除了1-4之外其他的字符,因为如果出现1-4的数字,在到*之前就会被拦下。

 

)是必不可少的。

 

 

 

每个选择后面的双分号和最后的esac都必不可少,,不过双分号可以不另起一行,根据上面错误提示,esac是一个文件的结束符。其实esac就是case反过来和if以fi结束的道理是一样的。那么有了case我们前面安装LAMP的代码就可以变得更简洁一点,不用再用那么多if了。

但是前面的那么菜单还需要我们自己打印出来,还是觉得有点费事,能不能自动生成菜单呢?还真的可以,这就是select语句。

 

里面提到一个PS3变量,那么我们先稍微了解一下,顺带了解一下PS1,PS2。参考了

https://blog.csdn.net/qq_37187976/article/details/79267430

 

 

 

 

 

 

 

上面的改变方式也只是一次性的,因为并没有写在/etc/bashrc等配置文件里面。我们还发现PS3的赋值要写在select前面才有效果,不然应该会在下一次select才会生效。那么下面来优化一下LAMP脚本吧。

 

效果:

 

还是不错的。

跳出循环

 

 

这个-x是调试时候用的,可以看到脚本执行的一个过程。break这上面的脚本里可以用exit代替,这是因为整个脚本在while后面没有语句了,如果有语句的话,是有巨大差别的,因为break只是跳出循环而已,while后面的命令还会执行,而exit是退出子shell,后面即使有命令也不会执行,直接回到了当前shell。

 

 

continue我觉得我下面据这个例子更好一点。

 

今天就先到这里了。

猜你喜欢

转载自blog.csdn.net/qq_41740705/article/details/81813138