上一篇文章介绍的是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脚本的调试
- 调试自己写的脚本的方法有很多,这里小编送大家一张图: