上一篇文章介绍的是shell的基础知识:https://blog.51cto.com/14048416/2355550   有人会说,shell简单啊,就是一些命令的堆砌啊,是的一些简单的操作仅仅执行几个命令就行了,但是相对一些复杂的业务和要求下,如果只能做到命令的堆砌,那也太有损shell的名声了。   小编通过一个例子给大家介绍,如果没有逻辑和条件,只是命令的堆砌,那么对维护Linux,是多么大的灾难。


#需求:清除/var/log 下message 日志文件 命令堆砌

#!/bin/bash       #shell 脚本的标识
cd /var/log       #进入/var/log
cat /dev/null > message  #清空message日志
echo Logs cleaned up’ #打印清空成功

缺陷

  • 如果不是root用户不能执行进入/var/log ,和清空message文件
  • 不论是否成功下面的命令都继续执行
  • 不论脚本是否成功,都会输出‘clean up’

修改后的脚本

#!/bin/bash       #shell 脚本的标识
LOG_DIR=/tmp/log  #设置一个变量,名称为/tmp/log
ROOT_UID=$UID    #设置一个变量为当前的用户的uid
if [ "$UID" -ne 0  ]   #判断是否是root,root用户的uid为0
  then 
    echo "Must be root tu run this script" #如果不是输出,并异常退出脚本
    exit 1
fi
cd $LOG_DIR || {   #如果是root,在判断是否可以进入/tmp/log
    echo "Cannot change to necessary directory." >&2  #如果不能进入,则也异常退出
    exit 1
}
cat /dev/null>message && echo "Log clean up."  #最后清空之后,输出一句话并正常退出
exit 0

有些初学者,看见上面的脚本,可能觉得,有些看不懂,没关系,接下来,小编将会一一讲解上面脚本的知识点!

一、条件测试

  在bash的各种流程控制结构中通常要进行各种测试,然后根据测试结果执行不同的操作,有时候也会通过if流程控制语句相结合,使我们可以方便的完成判断。 语法

格式1:test<测试表达式>
格式2:[ 测试表达式 ]
格式3:[[测试表达式]]

提示:在[[]]中,可以使用&&、||、>、<等操作符进行复杂判断,但是不能应用于[]

(1)test条件测试

#使用方法:
test -f file   判断是否是文件
例:test -f file && echo 1 || echo 0    #判断文件是否存在,存在返回 1 不存在返回0

test ! -f  file   判断文件是否不存在
例:test !-f file && echo 1 || echo 0    #判断文件是否存在,不存在返回1  存在返回0

test -d dir 判断是否是目录

(2)[]条件测试

#使用方法:
[-f file]  普通文件是否存在
例:[-f file ] && echo 1 || echo 0    #判断文件是否存在,存在返回 1 不存在返回0
[ ! -f file ]   判断文件是否不存在
例:[-f file] && echo 1 || echo 0    #判断文件是否存在,不存在返回1  存在返回0

[-d dir]  #判断是否是目录
-s # 判断文件是否存在并且不为空
-e #判断文件是否存在,区别于-f,-e表示所有文件
-r #判断文件是否存在并且是否可读
-w #判断文件是否存在并且是否可写
-x #判断文件是否存在并且是否可执行
-L #判断文件是否存在并且是否为链接文件
f1 -nt f2  #判断是否f1比f2新
f1 -ot f2  #判断是否f1比f2久

(3)[[]]条件测试

#使用方法:
	[[-f file]]  文件是否存在
例:[[-f file ]] && echo 1 || echo 0    #判断文件是否存在,存在返回 1 不存在返回0
	[[ ! -f file ]]   判断文件是否不存在
例:[[-f file]] && echo 1 || echo 0    #判断文件是否存在,不存在返回1  存在返回0

(4)字符串测试操作符

  注意:在使用字符串进行比较时,最好使用””双引号将其扩起

(5)整数操作符

(6)逻辑操作符

(7)条件测试的举例

  上面讲了诸多概念,现在通过具体的例子让大家看看条件测试具体怎么用:

 #这里声明两个变量:file1=/etc/password   file2=/etc/service
 [-f “$file1”] && echo 1||echo 0        $file1存在返回1,不存在返回0
 [-d “$file1”] && echo 1||echo 0       $file1是目录返回1,不是目录返回0
 [-s “$file1”] && echo 1||echo 0       $file1是文件并且不为空返回1,否则返回0
 [-e “$file1”] && echo 1||echo 0       $file1存在返回1,不存在返回0
 [-w “$file1”]&&echo 1 ||echo 0        $file1存在并且可写,返回1,不存在返回0
 [-x “file1”]|| exit 5                    判断$file1是否可执行,不可执行异常退出
#对字符串的操作
[-z “$value”] || value=”…”           判断变量是否长度为0,如果为0,赋初值
[“$networkworking”!= “yes”]&&exit 6  通过对变量的判断,确定是否执行脚本后面的代码
#复合条件测试
[-f “$file1”-o -e “$file2”] &&echo 1 ||echo 0   file1是文件或者file2存在返回1,否则0
[-f “$file1”-a -e “$file2”] &&echo 1 ||echo 0   file1是文件并且file2存在返回1,否则0
[[-f “$file1”&& -e “$file2”]] &&echo 1 ||echo 0   file1是文件或者file2存在返回1,否则0
[[-f “$file1”|| -e “$file2”]] &&echo 1 ||echo 0   file1是文件并且file2存在返回1,否则0
#整数测试
[ 1 -eq 2 ] &&echo 1||echo 0    等于
[ 1 -gt  2 ] &&echo 1||echo 0    大于
[ 1 -lt  2 ] &&echo 1||echo 0    小于
[ 1 -le  2 ] &&echo 1||echo 0    小于等于
[ 1 -ge  2 ] &&echo 1||echo 0    大于等于
[ 1 -ne  2 ] &&echo 1||echo 0    不等于
注意:及时这里的两个数组都是字符串的形式,依然可以比较。
#判断条件后面执行多条命令
file=/etc/profile
file_bak=${file}.bak
[ -f $file ] && {
cat $file
cp $file $file_bak
mv $file_bak ~/
}

二、控制流程语句

   当然,仅仅学习了条件测试,就可以完成很多的功能,但是如果业务相当复杂,功能相当繁多,只是使用条件测试话,不仅完不成具体的功能,对代码的可读性也很差。为了胜任更高的要求,接下来介绍控制流程语句,可以说shell有了它,如鱼得水吧。

(1)if语句

单分支

 #语法:
 If  [条件]
  then 
命令;
命令;
….
   fi
或者:if[条件];then 命令…; fi

#例:比较大小

num1=10
num2=23
if [ $num1 -gt $num2 ]
  then
    echo "$num1"
else
    echo "$num2"
fi

#判断系统内存大小,如果小于100M就报警
cur_free=`free -m|awk ' /buffers\// {print $NF}'`
chars="current memory is $cur_free"
if [ $cur_free -lt 100 ]
  then 
    echo "$chars"
    echo "momory is unfree!"
fi

多分支

 #语法:
 多分支:
If  [条件]
  then 
命令;
命令;
….
     else
命令;
命令;
….
   fi
#例:判断文件是否存在,不存在就创建
 file=/tmp/zy/zy.txt
if [ -f "$file" ]     
  then
    ll $file
  else
    touch $file
fi

if-elif-elif...-else: #语法:

if [条件]
  then
命令;
命令;
  elif [条件]
  then
命令
  else
命令
fi

#例:比较两个数的大小

read -p "pls input two numbers:" a b
if [ $a -gt $b ]
  then
    echo "$a > $b"
  elif [ $a -eq $b ]
  then
    echo "$a == $b"
  else
    echo "$a < $b"
fi

(2)case语句

 #语法:
 case:
语法:
case “字符串变量”in
pat1) 指令;;
pat2) 指令;;
pat3) 指令;;
*)指令
;;	
esac

#例:

read -p "Please you input is a num:" num
case "$num" in
9[0-9])  #范围查找
echo A;;
8[0-9])
echo B;;
*)
echo C;;
esac

(3)for循环

for 循环,具体的使用方法有很多,这里我们使用三个例子说明:

 #例1:(命令作为循环内容)
 for i in `seq 10`
  do
echo $i
done
 #例2:(序列作为循环内容)
 sum=0
for i in {1..100}
  do
    let sum=sum+i
done
echo $sum
#例3:(集合算术操作符的for)
sum=0
for((i=1;i<=100;i++))
  do
    let sum=sum+i
done
echo $sum

(3)while循环

#语法
while [expression]
  do
指令
done

#举例:

#例1
i=1
sum=0
while ((i<=100))
  do
    let sum=sum+i
    let i++
done
echo $sum

#例2
i=1
sum=0
while [ $i -le 100 ]
  do
    let sum=sum+i
    let i++
done
echo $sum

(4)until循环

#语法
unitl [ expression]
  do
commond;
done

#举例:

sum=0
i=1
until [ $i -gt 100 ]
  do
    let sum=sum+i
    let i++
done 
echo $sum

注意:unit和for、while的区别就是,until是条件满足时退出循环,而while和for是条件不满足是退出循环。

三、shell中的数组

(1)数组的定义:

方法1:array=(value1 value2 valu3 value4)
方法2:array=([index1]=value1 [index2]=value2 [index3]=value3)
方法3:array[0]=value1 array[1]=value2 array[2]=value
方法4:使用命令的方式定义数组:array=($(ls))   将ls命令的执行结果当做数组的值

(2)数组的增、删、改、查

arr=(1 2 3 4 5 6 7)  #数组的声明,并赋值
#增
arr[index]=value    #给数组增加元素,index:下标(任意)
#删
unset arr[index]     #删除相应index位置的元素
unset arr           #删除整个数组
#改
arr[index]=value    #给数组增加元素,index:下标(该位置已存在值)
#查:
echo ${arr[index]}   #获取数组单个元素,index表示数组的下标
echo ${arr[\*]}         #获取数组的所有元素
echo ${arr[\@]}      #获取数组的所有元素
echo ${!arr[\*]}        #获取数组的所有下标
echo ${#arr[@]}                     #查看数组的长度

(3)数组的高级操作

数组内容的截取

语法:${arr[*]:number1:len} 从下标number1开始取,取长度为len,获得一个新的数组。  

[root@test zy]# arr1=(1 2 3 4) [root@test zy]# echo ${arr1[*]:0:2} #从下标为0开始取,取两个元素

数组的替换

语法:${arr[*]/valu1/valu2} #将数组中的某个值,替换成其他值(完全替换),生成新数组,数组本身不变。   : >[root@test zy]# arr1=(aa aa bb cc dd) >[root@test zy]# echo ${arr1[*]/aa/bb} #打印bb bb bb cc dd

数组的合并

语法:arr=(${arr1[*]} ${arr2[*]}) #arr1和arr2分别是两个数组   : >[root@test zy]# arr1=(1 2 3) >[root@test zy]# arr2=(4 5 6) >[root@test zy]# arr=(${arr1[]} ${arr2[]}) >[root@test zy]# echo ${arr[*]} #打印:1 2 3 4 5 6

数组的遍历

#使用下标方式遍历:
arr=(1 2 3 4 5 6)
for i in “${!arr[*]}”
  do
echo “${arr[$i]}”
done
#使用数组的值遍历
for i in “${arr[*]}”
  do
echo “$i”
done

(4)数组的存储

注意:Shell中的数组的存储方式不同,如果在相应的下标中无元素,则无此下标。

四、Shell中的函数

语法

#简答语法:
函数名(){
指令…
return n
}
#标准语法
function 函数名(){
指令…
return n
}

shell函数的执行

 #例子
   #!/bin/sh
  #无参数
  function func_test1(){
     return 2
   }
   #有参数
   function func_test2(){
      let  sum=$1+$2   #这里的$1和$2暂时作为函数的参数
           return $sum  
  }
  #调用参数函数
  func_test1
  echo "$?"
  #调用有参数的函数
  func_test2 1 2
  echo "$?"  #$?用于接受函数的返回值

自定义函数库

#第一步:编写一个脚本(函数库):
#!/bin/sh
function func1(){…}
function func2(){…}
function func3(){…}
#第二步:(在其他脚本中加载这个函数库)
#!/bin/sh
source  ./函数库名     #下载函数库
func1()    #可以使用函数库中的函数

函数使用的注意事项:     - 在函数体中,位置参数($1、$2、$3、$#、$*、$@、$?)都可以是函数的参数     - 父脚本的参数则临时的被函数参数所覆盖(相当于局部变量)     - $0 仍然是父脚本的名称     - return 关键字用于跳出函数     - 函数调用不能在函数创建之前     - 函数的返回值只能是数字,不然会报错 :numeric argument required

五、Shell脚本的调试

    - 调试自己写的脚本的方法有很多,这里小编送大家一张图: