Linux shell 整理之 语法结构篇(五)


前置参考文章

Linux shell 整理之 基本概念篇(一)

Linux shell 整理之 基本概念篇(二)

Linux shell 整理之 用户权限篇(三)

Linux shell 整理之 复合命令行篇(四)

背景

结构化语句指的是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 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章