文章目录
前置参考文章
背景
结构化语句指的是shell编程允许改变shell的正常的执行流.比如 条件分支语句(if case),循环语句(for while until).其实更多的个高级语言,都是有这些语法特性的,学过一般的程序语言,就能够很方便地理解这块内容.
条件分支语句
if条件分支
if then 最基本的语句
格式1
zhangll 23:15:20 linuxshell$ cat ifthen2.sh
#!/bin/bash
if pwd;then
echo "zhangll"
fi
zhangll 23:15:30 linuxshell$ ./ifthen2.sh
/home/zhangll/linuxshell
zhangll
格式2
zhangll 23:17:11 linuxshell$ cat ifthen.sh
#!/bin/bash
if pwd
then
echo "zhangll"
fi
zhangll 23:17:17 linuxshell$ ./ifthen.sh
/home/zhangll/linuxshell
zhangll
在一般的编程语言的条件判断语句中if 后面跟的是逻辑条件判断,但是在shell中并不是这样,而是在if之后跟着是命令执行之后的退出状态码,如果退出码是0,则执行then后面的语句,否则进入其他分支
if then else
zhangll 23:22:25 linuxshell$ cat ifthenelse.sh
#!/bin/bash
if ls nofile
then
echo "has file"
else
echo "no file"
fi
zhangll 23:22:31 linuxshell$ ./ifthenelse.sh
ls: 无法访问'nofile': 没有那个文件或目录
no file
可以发现ls nofile会输出错误信息,因此不会走then,而是最走 else 分支
if then elif then else
当有条件的时候需要使用elif then来做更多的判断
zhangll 23:27:48 linuxshell$ cat ifthenelifelse.sh
#!/bin/bash
if ls nofile
then
echo "has file"
elif ls .
then
echo "has ."
else
echo "no file"
fi
zhangll 23:28:05 linuxshell$ ./ifthenelifelse.sh
ls: 无法访问'nofile': 没有那个文件或目录
abc ee ifthenelifelse.sh pid2.sh testing test.log
abc.txt iftest.sh ifthenelse.sh pid.sh testing] var2
compare.sh ifthen2.sh ifthen.sh sorted.txt Testing] var2]
has .
当你觉得错误信息打印太多太乱,你可以使用exec永久重定向输出,这里可以查看之前的文章
case
case一般与模式匹配相关
zhangll 23:38:55 linuxshell$ cat case.sh
#/bin/bash
case $USER in
zhanll | zhangl)
echo "hello z";;
(zhang*l) #这里特意指出来()也可以作为匹配条件使用
echo "hello zll";;
*)
echo "hello default";;
esac
zhangll 23:38:59 linuxshell$ ./case.sh
hello zll
test处理特殊条件
在shell中提供了test方法,用来判断条件是否满足
一旦满足就会退出返回0退出码,否则返回非0退出码
这样就很容易使用逻辑比较做测试
使用[]来代替test函数
也即是说在使用[condition]的时候等价与test condition
test支持如下三种条件测试
1)数值判断
相等(equal)不相等(not equal)判断
-eq
-ne
大于(greater then)与小于(less then)
-gt
-lt
大于等于(greater equal)与 小于等于(less equal)
-ge
-le
zhangll 23:57:07 linuxshell$ cat equal.sh
#!/bin/bash
var1=10
var2=20
if test $var1 -gt $var2;then
#if [ $var1 -gt $var2 ];then
echo "$var1 is greater then $var2"
else
echo "$var1 is less then $var2"
fi
zhangll 23:57:15 linuxshell$ ./equal.sh
10 is less then 20
2)字符串判断个
只介绍逻辑判断符,判断逻辑同哦你
逻辑判断 | 描述 |
---|---|
str1 = str2 | 检查 str1 是否和 str2 相同 |
str1 != str2 | 检查 str1 是否和 str2 不同 |
str1 < str2 | 检查 str1 是否比 str2 小 |
str1 > str2 | 检查 str1 是否比 str2 大 |
-n str1 | 检查 str1 的长度是否非0 |
-z str1 | 检查 str1 的长度是否为0 |
3)文件判断
逻辑判断 | 描述 |
---|---|
-d file | 检查 file 是否存在并是一个目录 |
-e file | 检查 file 是否存在 |
-f file | 检查 file 是否存在并是一个文件 |
-r file | 检查 file 是否存在并可读 |
-n str1 | 检查 str1 的长度是否非0 |
-s file | 检查 file 是否存在并非空 |
-w file | 检查 file 是否存在并可执行 |
-x file | 检查 file 是否存在并可执行 |
-O file | 检查 file 是否存在并属当前用户所有 |
-G file | 检查 file 是否存在并且默认组与当前用户相同 |
file1 -nt file2 | 检查 file1 是否比 file2 新 |
file1 -ot file2 | 检查 file1 是否比 file2 旧 |
高级判断
复合判断
(( expression )) 高级数值比较
双圆括号不仅支持比较运算(> ;< ;!= ;=),但是不支持(-eq -ne -gt -lt …),同时也支持赋值操作
zhangll 22:12:17 linuxshell$ cat -n doubleI.sh
1 #/bin/bash
2
3 var1=20
4 var2=3
5 if (( $var1 > $var2 ));then
6 (( var3 = $var1 + $var2 ))
7 echo "$var1 is bigger than $var2, sum is $var3"
8 else
9 echo "$var1 is smaller than $var2"
10 fi
zhangll 22:12:22 linuxshell$ ./doubleI.sh
20 is bigger than 3, sum is 23
[[ expression ]] 高级字符串比较
双方括号中的表达式是针对字符串的,支持一切test支持的字符操作,但是多了一个特性.模糊匹配(==)
在shell中使用(=)来比较字符ascii值的大小,并不是一般的赋值操作.这个非常重要
因此我们用(==)来作为模糊匹配就很合理了
zhangll 21:59:06 linuxshell$ cat -n doubleF.sh
1 #/bin/bash
2
3 if [[ $USER == z* ]];then
4 echo "hello $USER"
5 else
6 echo "no"
7 fi
zhangll 21:59:14 linuxshell$ ./doubleF.sh
hello zhangll
扩展内容 $[ operation ]
在shell编程中 $[ operation ]其实是对expr命令 的一种替代,不过expr支持更多的操作,或者通过
man expr
,不过前者更加受欢迎,类似于 条件语句中的[]操作替代test命令一样
man test
循环语句
循环结构也是非常常见的,除了分支倒流之外,还可以做闭环处理,不断处理同一逻辑,循环结构的发明本身就是非常了不起的
for
基本语法
zhangll 22:19:04 linuxshell$ cat -n for1.sh
1 #!/bin/bash
2
3 var="a : b : c : d"
4
5 for i in $var
6 do
7 echo line:$i
8 done
zhangll 22:19:07 linuxshell$ ./for1.sh
line:a
line::
line:b
line::
line:c
line::
line:d
稍微复杂一些的结构
当遍历的数组有单引号或者特殊字符时候的处理方法
1 使用反斜杠转意字符
2 用双引号包裹字符 (这里特别指出,双引号并不占用内存空间,只是提醒编译器在被包裹的字符串作为一个整体被处理)
zhangll 22:30:01 linuxshell$ cat -n for1t.sh
1 #!/bin/bash
2
3 for i in I\'m "zhangll'" , we are family
4 do
5 echo line:$i
6 done
zhangll 22:32:13 linuxshell$ ./for1t.sh
line:I'm
line:zhangll'
line:,
line:we
line:are
line:family
for循环注意的双引号问题
先见如下语句
zhangll 22:53:15 linuxshell$ cat -n for2t.sh
1 #!/bin/bash
2 var="I'm zhangll' :, we are family"
3 #IFS=$'\n':;" # 因为换行符是两个字符所以用单引号括起来
4 for i in $var
5 do
6 echo line:$i
7 done
8 echo "##############$i"
9 #IFS=$IFS.OLD #恢复默认分隔符
10 for i in "I'm zhangll' , we are family"
11 do
12 echo line:$i
13 done
在第一个for循环中,我们用双引号声明了一个字符串组,其实for循环会根据默认的IFS(默认是空格或者制表符(\t)或者换行符(\n))作为内部字段分隔符,我们可以在使用for循环之前使用IFS=\n:表示以换行和冒号作为分隔符,可以自己测试一下
在第二个for循环我们直接使用双引号使得"I’m zhangll’ , we are family"做为一个整体使用
结果
zhangll 22:40:01 linuxshell$ ./for2t.sh
line:I'm
line:zhangll'
line:,
line:we
line:are
line:family
##############family
line:I'm zhangll' , we are family
这里还会发现,for中的循环变量可以在for循环外部使用,这个非常特别
练习题目
for i in "./*"
do
echo file name : $i
done
这个结果会是怎么样的呢?
for循环与重定向的结合
上一章中我们了解了重定向相关的知识,现在有个需求,我们只想把for循环内部打印到指定文件夹中如何处理?
语法 done >filename 等价 done 1>filename
默认1为输出重定向,还记得男人的比喻吗?
zhangll 23:09:14 linuxshell$ cat -n for4t.sh
1 #!/bin/bash
2 for (( i = 0; i < 10; i++ ))
3 do
4 echo $i
5 #done > $0.log
6 done 1> $0.log
7 echo "外部使用到$i"
zhangll 23:09:20 linuxshell$ ./for4t.sh
外部使用到10
zhangll 23:09:26 linuxshell$ cat for4t.sh.log
0
1
2
3
4
5
6
7
8
9
while
while语句相当于下面语句的缩写版本
#伪代码,但不妨碍理解
for (;if test command;);
do
do something....
done
缩写为
while test comman
do
do something
done
这就是对while最精髓地解.当一个条件不满足的时候才退出内部的代码
特别留意
#!/bin/bash
#for(;test 1;);
count=10
count2=110
while echo $count $count2
[ $count2 -ge 0 ]
[ $count -ge 0 ]
do
count=$[$count - 1]
count2=$[$count2-1]
echo "count : $count"
#sleep(1)
done;
while支持多条件只有在最后一个语句退出码为非0的时候才退出(每个test语句占一行),因此要特别小心最后一个语句退出码永远都为0的情况
until
他是while的反面 ,语法与while一致
循环控制
关键词 break continue
在循环控制中,break和continue默认分别控制者打断最近一层循环和继续最近一层循环的作用
(base) zhangll 22:29:40 linuxshell$ cat -n ./while2.sh
1 #!/bin/bash
2 #for(;test 1;);
3 count=3
4 count2=110
5 while echo $count $count2
6 [ $count -ge 0 ]
7 do
8 while [ $count2 -ge 0 ]
9 do
10 count2=$[$count2-1]
11 echo " 内层循环count2: $count2"
12 if (($count2 >= 99 ))
13 then
14 break
15 fi;
16 done;
17 count=$[$count - 1]
18 echo "外层循环count : $count"
19 #sleep(1)
20 done;
(base) zhangll 22:29:49 linuxshell$ ./while2.sh
3 110
内层循环count2: 109
外层循环count : 2
2 109
内层循环count2: 108
外层循环count : 1
1 108
内层循环count2: 107
外层循环count : 0
0 107
内层循环count2: 106
外层循环count : -1
-1 106
为什么说是默认呢?因为break关键词默认会被当作
break 1
业就是代表最近一层循环,当然如果值为 break 2,代表退出最近的第二层循环
continue语法与break 一致
高级应用
user.csv文件中每行对应姓名与年龄(以逗号分割),现在需要分割文件中的字符串,并输出所有年龄大于20岁的人,并以新的格式输出到outer文件中,此时使用了重定向,while 多test命
(base) zhangll 22:59:07 linuxshell$ cat user.csv
zhangll01,20
jianglina02,30
zhutou,39
(base) zhangll 22:59:09 linuxshell$ cat -n ./whileHight.sh
1 #!/bin/bash
2 inputfile="user.csv"
3 outputfile="outer"
4 while IFS=','
5 read -r name age
6 do
7 if (( $age > 20 ))
8 then
9 echo "name : $name,age: $age "
10 fi
11 done < $inputfile > $outputfile
(base) zhangll 22:59:15 linuxshell$ ./whileHight.sh
(base) zhangll 22:59:23 linuxshell$ cat outer
name : jianglina02,age: 30
name : zhutou,age: 39 39