第十三节.shell脚本学习(二)

最后更新于 2021-10-09 484 次阅读


1.脚本中的测试判断式

在shell脚本中,我们除了使用【$?】这一变量的返回值进行判断以外,还有【test】和中括号【[]】这两个测试判断式可以进行条件判断

(1)test命令

test这个命令用于条件判断的话,可谓是十分的好用,但是我们在直接使用test命令,例如test -e 来判断一个文件是否存在的时候,系统并不会直接给我们输出它的返回值。

test命令返回值演示

但是我们可以配合相应的【&&】和【||】命令来展示其结果。

test配合【&&】和【||】的演示
test判断脚本内容展示

(2)判断符号【[ ]】

【[ ]】中括号这个符号和test命令的使用效果是一致的,不过使用【[ ]】作为判断符号有3个必须要注意的地方就是:

①中括号的两端需要有空格符来分隔。

例:[ "${1a}" == "${2a}" ],注意变量和括号间隔的空格

括号内的变量最好有【“ ”】双引号括住

括号内的常数最好有单引号【‘ ’】或双引号【“ ”】括住

例:[ "${1a} " == "2" ]

例如,我想判断一个变量'input'是不是空变量,如果是的话就返回一个“这是一个空变量”,如果不是的话就输出“这是非空变量”,我应该怎么写这个判断式子?

使用【test】命令,我们可以写成:

test -z ${input} && echo '这是一个空变量' || echo '这是非空变量'

test -z命令演示

而使用中括号【[ ]】,我们可以写成:

[ -z "${input}"] && echo '这是一个空变量' || echo '这是非空变量'

【[ ]】中括号命令演示

(3)判断式常用参数

① 【文件类型】的判断

关于某个文件名的【文件类型】的判断,例如

test -e filename 判断文件是否存在

Ⅰ. -e

判断是否存在拥有该【文件名】的文件

Ⅱ. -f

该【文件名】是否存在,且是否为文件?

Ⅲ. -d

该【文件名】是否存在,且是否为目录?

② 【文件权限】的判断

关于某个文件名的【文件权限】的判断

例如[ -r "filename" ] 判断该文件是否存在,且是否可读

Ⅰ. -r

该【文件名】是否存在,且是否可读?

Ⅱ. -w

该【文件名】是否存在,且是否可写?

Ⅲ. -x

该【文件名】是否存在,且是否可执行?

Ⅳ. -u

该【文件名】是否存在,且是否拥有SUID属性

Ⅴ. -g

该【文件名】是否存在,且是否拥有SGID属性

③ 【文件之间比较】的判断

用于比较两个文件之间的不同

例如 test file1 -nt file2,比较第一个文件是否比第二个文件新

Ⅰ. -nt(newer than)

test file1 -nt file2

比较file1是否比file2新

Ⅱ. -ot (older than)

test file1 -ot file2

比较file1是否比file2旧

④【两个整数之间比较】的判断

用于比较两个整数的大小关系

例如:test n1 -eq n2

Ⅰ. -eq(equal)

比较两个数值是否相等

Ⅱ. -ne(no equal)

比较两个数值是否不等

Ⅲ. -gt(greater than)

比较n1是否大于n2

Ⅳ. -lt(less than)

比较n1是否小于n2

Ⅴ. -ge(greater than or equal)

比较n1是否大于等于n2

Ⅵ. -le(less than or equal)

比较n1是否小于等于n2

⑤ 【字符串数据】的判断

用于判断字符串是否是空字符串,两个字符串是否相等

例如:test -z ${PATH},判定PATH是否是空变量

Ⅰ. -z

判断字符串是否为空字符串,若是,则为true

Ⅱ. -n

test -n string

判断字符串是否为空字符串,若是,则为false

(-n可省略,区别-z参数)

Ⅲ. ==

test str1 == str2

判断str1是否和str2相等,若相等,则为true

Ⅳ. !=

test str1 != str2

判断str1是否和str2不相等,若不相等,则为true

⑥ 【多重条件】的判断

和(and)、或(or)、反选(!)等条件的使用

例如:test -r file1 -a -x file2

当file1有可读权限并且file2有可执行权限时,才返回true

Ⅰ. -a(and)

test -z file1 -a -z file2

和(and)两条件同时成立,才返回true

Ⅱ. -o(or)

test -z file1 -o -z file2

或(or)两条件只要达成一个,就返回true

Ⅲ. !

test ! -x file1

反相选择,如果file1具有可执行权限,则返回true

!=反选演示

2.脚本的默认变量和特殊变量

(1)默认变量

我们通过之前的学习能够得知,再输入命令时,我们可以在命令后面带上选项和参数,那么脚本在执行的时候,我们能不能接上相应的参数呢?

答案是可以的,并且脚本后续所带的参数,我们是可以在脚本的内容里进行指定的,通过直接输入参数来执行脚本,就可以省去一些手动输入变量的操作。

而这个脚本的后续参数,其实就是脚本自身的默认变量($0 $1 $2....),具体的我们可以通过一个脚本执行来看看:

例如:sh ping.sh opt1 opt2 opt3 opt4

则默认变量为 $0 $1 $2 $3 $4

脚本的文件名就是$0这个变量,然后后续的参数依顺序分别对应变量$1、$2、$3.....

(2)特殊变量

除了上述的基本默认变量以外,我们还可以在脚本的编写中使用一些特殊变量:

① $#

代表后接参数的个数,上边的例子,$#的值为【4】

② $@

可以列出全部的参数,格式为【"$1""$2""$3""$4"】

③ $*

同样也可以列出全部的参数,但是格式为【“$1c$2c$3c$4”】,其中【c】为分隔符,默认为空格,上边的例子显示则为【“$1 $2 $3 $4”】

例如:如下这个脚本内容可以显示我们执行脚本时输入的选项对应的变量

显示脚本内置变量的脚本内容演示

其执行结果为:

脚本运行结果展示

3.脚本中的条件判断式

(1)if.... then....fi

当符合某个条件判断(if)时,就予以进行某任务(then).....,但是要注意在条件命令的结尾,要加上【fi】来表示命令结束

① 单层、简单条件判断式

如果只有一个条件的话,可以直接使用if....;then...fi来进行命令编写

语句格式:if [条件判断式]; then (如果是用test命令,则不加中括号【[ ]】)

条件达成要执行的任务

fi ←==将if反过来写,代表结束之意

例如:if test -z ${path}; then

echo "变量内容是空的!"

fi

然后在中括号[条件判断式]的写法可以参考第一节中的内容,如果包含多个想要达成的条件,除了使用【-a】或者【-o】的参数以外,也可以使用【&&】和【||】这两个,但是表达的意思和管道命令中的意思不同,【&&】也代表着【和】的意思,【||】代表着【或】

例如:[ "y" == "4" -o "z" == 5 ]可以写成:[ "y"==4 ] || [ "z"==5 ]

② 多重、复杂条件判断式

如果含有多个条件,可以使用 if...;then elif....;then elif...;then else.... fi 这个命令来进行编写

语句格式:if [第一个条件]; then

第一个条件达成执行的任务

elif [第二个条件]; then

第二个条件达成执行的任务

else

第一个和第二个条件都不达成时执行的任务

fi

脚本演示:

例1:if....;then脚本内容演示

脚本内容演示1

例2:带参数的脚本演示

脚本内容演示2
脚本输出结果展示2

例3:使用if判别式的系统端口判断脚本内容演示

端口检测脚本内容演示3
端口检测脚本输出结果演示3

(2)case...esac

case...esac和if...then...fi类似,也是一种条件判断式。如果说if...then....fi是用【比对】的方式对变量进行判断,符合状态就进行某些操作,并且通过多层次(.....elif)来进行多个变量的程序代码编写。那么case...esac就是用于多个【既定】的变量内容状态的设定(【既定】说的是固定数量的参数,比如一个代码我固定了其在执行时必须要输入的后续参数,假设是两个参数,那么除去默认变量的$0,就还有两个变量,分别是$1和$2)。假设脚本有1个参数,参数的内容我指定为【hello】和【bye】,那我就可以用case...esac来分别指定参数为【hello】时和【bye】时返回的信息。

语句格式:case ${变量内容} in

“第一个变量内容”)←变量的内容要双引号【“”】来括住,关键字为右边的括号

第一个程序段

;; ←每个变量内容结束时,要输入两个连续的分号【;;】

“第二个变量内容”)

第二个程序段

;;

*) ←最后一个变量的内容使用星号【*】来代表所有其他值

除了第一和第二个变量内容以外的程序执行段

exit 1

;;

esac ←case判断式的结尾要用esac来结尾

例1:我想创建一个脚本,当用户输入【hello】这第一个参数的时候,就显示“hello,how are you”;当用户没有输入参数时,显示“请输入参数”;当输入其他参数时,显示“请输入参数(hello)”

程序:case $1 in

"hello")

echo "hello,how are you"

"")

echo “请输入参数”

*)

echo “请输入参数(hello)”

esac

case...esac脚本内容演示
case...esac脚本结果演示

case的参数变量内容除了跟随脚本执行时一起输入以外,也可以配合read命令来让用户进行输入

例如这样的程序:这个程序能将输入的第一个数字参数从小写转为大写

read -p ‘请输入英文数字’ num

case ${num} in

“one”)

echo ‘你输入的是ONE’

;;

“two”)

echo '你输入的是TWO'

;;

"three")

echo '你输入的是THREE'

;;

*)

echo '请输入(one)或(two)或(three)'

;;

esac

(3)function(函数功能)

① 了解function

在脚本的编写中,一些重复的命令可以使用function(函数)功能进行一个命令简化,那啥是function功能?

function功能就相当于我特意编写一个专门执行某个特定命令的命令,来取代我脚本内重复的命令

命令格式:function 函数名 ( ) {

程序框

这里要注意一个问题,由于shell的脚本命令执行顺序是从上到下,从左到右,为了能够让脚本在执行时能找到我们所需的程序段,我们function一定要放在我们脚本程序的最前边,就有点像python里边的依赖那样

继续以(2)节里最后一个脚本来举例子,我们可以看出无论我们输入one还是two还是three,程序都是输出一段文字“你输入的是”,然后加上一个转换大小写后的英文数字。

那我们就考虑使用一个function来取代那个重复的文字输出,我们可以这样做:

function printit ( ){

echo "你输入的是"

case $1 in

"one")

printit ; echo $1 |tr [a-z] [A-Z]

;;

"two")

printit;echo $1 | tr [a-z] [A-Z]

;;

"three")

printit; echo $1 | tr [a-z] [A-Z]

;;

*)

echo '请输入(one)或(two)或(three)'

;;

esac

function + caes 脚本内容演示
function + caes 脚本结果演示

② function的内置变量

和脚本自带的内置变量同样,function也拥有自己的内置变量$0 、$1 、$2等,$0也代表着function名(function定义的函数名),但是$1,$2和之后的变量代表的意义是和脚本的内置变量是不同的。

这里我们用脚本来直接进行演示比较好说明

function内置变量脚本内容演示

这里我们创建一个名为【printit】的function,那么接下来调用的printit的第一个参数就是内置变量$1

function内置变量结果演示

4.不定循环

不定循环指的是不知道具体循环次数的循环,在shell中有两种不定循环的语句,一个是当满足条件时进行循环的【while...do...done】;一个是当满足条件就跳出循环的【until...do...done】

(1)while...do...done

【while】的意思是,当xx条件成立时,就进行循环,否则就停止循环

语句格式:while [条件判断式]

do←循环的开始使用do

程序段落

done←循环的结束使用done

例如:我想让用户输入一个2~100的之间的数字n,然后做一个1到n的累加。

read -p '请输入一个2~100之间的数字:' num

i=1

s=0

while [ "${i}" -lt "${num}" ]

do

s=$(($s+$i)) **←这里注意计算式的写法$((计算式))**

i=$(($i+1))

done

echo "累加的结果为:"${s}

while do done脚本内容演示
while do done脚本输出结果演示

(2)until...do...done

until...do...done和while...do...done的意义相反,其意义为:当满足xx条件时,就跳出循环,不然就一直循环

语句格式:until [ 条件判断式 ]

do

程序段

done

继续以上边的例子为例,我们写成until语句的话,就可以这样:

read -p '请输入一个2~100之间的数字:' num

i=1

s=0

until [ "${i}" -gt "${num}" ]

do

s=$(($s+$i))

i=$(($i+1))

done

echo "累加的结果为:"${s}

5.固定循环

【固定循环】指的是我已经知道了应该要循环的次数的状态。比如有一个数组,我反复的取里边的值出来,就是一种固定循环。

(1)for...do...done

语句格式:for var(变量) in $(seq 1 10)

do

程序块

done

这个语句的意思是,变量的内容在1、2、3....10这些内容中进行循环。$(seq 1 10)的是连续的意思,表示后续的两个数值的内容是一直连续的。

第一次循环中,$var的值为1;

第二次循环中,$var的值为2;

第三次循环中,$var的值为3;

.......

其中,变量的内容也可以是一个变量,例如:创建一个名为user的变量,其内容为user=$(cut -d ':' -f 1 /etc/passwd),也就是取出/etc/passwd这个文件的第一列的用户名。

然后我可以创建这么一个循环,从${user}变量中一次取出一个值进行循环,直到取尽其值。

for username(变量名) in ${user}

do

程序块

done

例如:我想创建一个脚本,能够自动帮我ping一个指定网段的IP是否畅通,我该如何用if...do...done来创建?

脚本内容:

read -p "请输入你想要查询的网段的网络地址(x.x.x):" netip

read -p "请输入你要查询的网段的起始主机地址:" beginip

read -p “请输入你想要查询的网段的结尾主机地址:” endip

echo "正在查询...请稍等"

for ip in $((seq ${begin} ${endip}))

do

ping -c 2 ${netip}.${ip} &>/dev/null && result=0 || result=1

if [ "${result} == “0” ]; then

echo "${netip}.${ip}这个网络地址通畅"

else

echo "${netip}.${ip}这个网络地址不通畅"

fi

done

for do done脚本内容演示
for do done脚本结果演示

6.shell脚本的跟踪和调试

我们在写完脚本后,有没有什么命令可以进行语法错误排查或运行跟踪排错呢?

答案是有的,我们可以使用【sh】命令带参数来运行脚本。

命令格式:sh 【-n v x】 脚本名

常用参数:

-n:不输出脚本内容,仅仅检查脚本语法有无错误,无错误则不进行输出

-v:脚本运行之前先输出脚本的内容,然后在依次运行脚本

-x:将使用到的脚本内容输出到屏幕上,查看脚本运行过程(好用)