Linux理论——Shell编程之循环语句与函数

前言

一、使用 for 循环语句

在实际工作中,经常会遇到某项任务需要多次执行的情况,而每次执行时仅仅是处理的 对象不一样,其他命令相同。例如,根据通讯录中的姓名列表创建系统账号,根据服务器清 单检查各主机的存活状态,根据 IP 地址黑名单设置拒绝访问的防火墙策略等。
当面对各种列表重复任务时,使用简单的 if 语句已经难以满足要求,而顺序编写全部代码更是显得异常烦琐、困难重重。本节将要学习的 for 循环语句,可以很好地解决类似问题。

1、for 语句的结构

使用 for 循环语句时,需要指定一个变量及可能的取值列表,针对每个不同的取值重复执行相同的命令序列,直到变量值用完退出循环。在这里,“取值列表”称为 for 语句的执行条件,其中包括多个属性相同的对象,需要预先指定(如通讯录、IP 黑名单)。
for 循环语句的语法结构如下所示。
在这里插入图片描述
上述语句结构中,for 语句的操作对象为用户指定名称的变量,并通过 in 关键字为该变量预先设置了一个取值列表,多个取值之间以空格进行分隔。位于 do…done 之间的命令序列称为循环体,其中的执行语句需要引用变量以完成相应的任务。
for 语句的执行流程:首先将列表中的第一个取值赋给变量,并执行 do…done 循环体中的命令序列;然后将列表中的第二个取值赋给变量,并执行循环体中的命令序列……依此类推,直到列表中的所有取值用完,最后将跳至 done 语句,表示结束循环,如图所示:
在这里插入图片描述

2、for 语句应用示例

(1)根据姓名列表批量添加用户

根据人事部门给出的员工姓名的拼音列表,在 Linux 服务器中添加相应的用户账号,初始密码均设置为“123456”。其中,员工姓名列表中的账号数量并不固定,而且除了要求账号名称是拼音之外,并无其他特殊规律。
针对上述要求,可先指定员工列表文件 users.txt,然后编写一个名为 uaddfor.sh 的Shell
脚本,从 users.txt 文件中读取各用户名称,重复执行添加用户、设置初始密码的相关操作。

[root@localhost ~]# vim /root/users.txt	//用做测试的列表文件
chenye
dengchao 
zhangjie
[root@localhost ~]# vim uaddfor.sh	//批量添加用户的脚本
#!/bin/bash
ULIST=$(cat /root/users.txt) 
for UNAME in $ULIST
do
useradd $UNAME
echo "123456" | passwd --stdin $UNAME &>/dev/null done
[root@localhost ~]# chmod +x uaddfor.sh
[root@localhost ~]# ./uaddfor.sh	//测试并确认执行结果
[root@localhost ~]# tail -3 /etc/passwd 
chenye:x:1005:1005::/home/chenye:/bin/bash 
dengchao:x:1006:1006::/home/dengchao:/bin/bash 
zhangjie:x:1007:1007::/home/zhangjie:/bin/bash

若要删除 uaddfor.sh 脚本所添加的用户,只需参考上述脚本代码,将 for 循环体中添加用户的命令序列改为删除用户的操作即可。例如,建立一个名为udelfor.sh 的脚本如下所示。

[root@localhost ~]# vim udelfor.sh	//批量删除用户的脚本
#!/bin/bash
ULIST=$(cat /root/users.txt) for UNAME in $ULIST
do
userdel -r $UNAME &>/dev/null done
[root@localhost ~]# chmod +x udelfor.sh
[root@localhost ~]# ./udelfor.sh	//测试并确认执行结果
[root@localhost ~]# id chenye
id: chenye: no such user	//提示无此用户

(2)根据 IP 地址列表检查主机状态

根据包含公司各服务器 IP 地址的列表文件,检查其中各主机的 ping 连通性,输出各主机是否启动、关闭。其中,服务器的数量并不固定,各服务器的 IP 地址之间也无特殊规律。
针对此案例要求,可先指定 IP 地址列表文件 ipadds.txt,然后编写一个名为 chkhosts.sh的 Shell 脚本,从 ipadds.txt 文件中读取各服务器的 IP 地址,重复执行 ping 连通性测试, 并根据测试结果输出相应的提示信息。

[root@localhost ~]# vim /root/ipadds.txt	//用做测试的列表文件
172.16.16.1
172.16.16.22
172.16.16.220
[root@localhost ~]# vim chkhosts.sh	//循环检查各主机的脚本
#!/bin/bash
HLIST=$(cat /root/ipadds.txt) 
for IP in $HLIST
do
ping -c 3 -i 0.2 -W 3 $IP &> /dev/null  
                     //-c 发送包的数量;-i 发送 ping 包间隔;-W 超时时间
if [ $? -eq 0 ] 
then
    echo "Host $IP is up."
else
    echo "Host $IP is down."
fi 
done
[root@localhost ~]# chmod +x chkhosts.sh
[root@localhost ~]# ./chkhosts.sh	//测试并确认执行结果
Host 172.16.16.1 is up.
Host 172.16.16.22 is up. 
Host 172.16.16.220 is down.

上述脚本代码中,do…done 循环体内嵌套使用了 if 条件选择语句,用来针对不同 IP 地址的测试结果进行判断,并输出相应的提示信息。嵌套可以理解为镶嵌、套用,就是在已 有的语句、函数中在多加一个或多个语句、函数等。实际上,if 语句、for 语句及其他各种Shell 脚本语句都是可以嵌套使用的,后续课程中将不再重复说明。

二、使用 while 循环语句

for 循环语句非常适用于列表对象无规律,且列表来源已固定(如某个列表文件)的场合。而对于要求控制循环次数、操作对象按数字顺序编号、按特定条件执行重复操作等情况, 则更适合使用另外一种循环——while 语句。

1、while 语句的结构

使用 while 循环语句时,可以根据特定的条件反复执行一个命令序列,直到该条件不再满足时为止。在脚本应用中,应该避免出现死循环的情况,否则后边的命令操作将无法执行。 因此,循环体内的命令序列中应包括修改测试条件的语句,以便在适当的时候使测试条件不再成立,从而结束循环。
while 循环语句的语法结构如下所示
在这里插入图片描述
while 语句的执行流程:首先判断 while 后的条件测试操作结果,如果条件成立,则执行 do…done 循环体中的命令序列;返回 while 后再次判断条件测试结果,如果条件仍然成立,则继续执行循环体;再次返回到 while 后,判断条件测试结果……如此循环,直到 while 后的条件测试结果不再成立为止,最后跳转到 done 语句,表示结束循环,如图 所示:
在这里插入图片描述
使用 while 循环语句时,有两个特殊的条件测试操作,即 true(真)和 false(假)。使用 true 作为条件时,表示条件永远成立,循环体内的命令序列将无限执行下去,除非强制终止脚本(或通过 exit 语句退出脚本);反之,若使用 false 作为条件,则循环体将不会被执行。这两个特殊条件也可以用在 if 语句的条件测试中。

2、while 语句应用示例

(1)批量添加规律编号的用户

在一些技术培训和学习领域,出于实验或测试的目的,需要批量添加用户账号,这些用户的名称中包含固定的前缀字串,并按照数字顺序依次进行编号,账号的数量往往也是固定的。例如,若要添加 20 个用户,名称依次为 stu1、stu2、…、stu20,可以参考以下操作。

[root@localhost ~]# vim uaddwhile.sh	//批量添加用户的脚本
#!/bin/bash
PREFIX="stu"
i=1
while [ $i -le 20 ] 
do
   useradd ${PREFIX}$i
   echo "123456" | passwd --stdin ${PREFIX}$i &> /dev/null 
   let i++
done
[root@localhost ~]# chmod +x uaddwhile.sh

上述脚本代码中,使用变量 i 来控制用户名称的编号,初始赋值为 1,并且当取值大于20 时终止循环。在循环体内部,通过语句“let i++”(等同于 i=expr $i + 1)来使变量 i 的值增加 1,因此当执行第一次循环后 i 的值将变为 2,执行第二次循环后 i 的值将变为 3,……, 依此类推。
测试并确认 uaddwhile.sh 脚本的执行结果如下所示。

[root@localhost ~]# ./uaddwhile.sh
[root@localhost ~]# grep "stu" /etc/passwd | tail -3 
stu18:x:1022:1022::/home/stu18:/bin/bash 
stu19:x:1023:1023::/home/stu19:/bin/bash 
stu20:x:1024:1024::/home/stu20:/bin/bash

若要删除 uaddwhile.sh 脚本所添加的用户,只需参考上述脚本代码,将 while 循环体中添加用户的命令序列改为删除用户的操作即可。

[root@localhost ~]# vim udelwhile.sh	//批量删除用户的脚本
#!/bin/bash
PREFIX="stu"
i=1
while [ $i -le 20 ] 
do
   userdel -r ${PREFIX}$i
   let i++
done
[root@localhost ~]# chmod +x udelwhile.sh
[root@localhost ~]# ./udelwhile.sh	//测试并确认执行结果
[root@localhost ~]# id stu20
id: stu20:无此用户	//提示无此用户

(2)猜价格游戏

中央电视台著名的“时尚购物街”节目中,有一个猜价格的互动环节,要求参与者在最短 的时间内猜出展示商品的实际价格,当所猜的价格高出或低于实际价格时,主持人会给出相 应的提示。下面以此环节为原型,编写一个猜价格的 Shell 脚本。
案例要求如下:由脚本预先生成一个随机的价格数目(0~999)作为实际价格,判断用户猜测的价格是否高出或低于实际价格,给出相应提示后再次要求用户猜测;一直到用户 猜中实际价格为止,输出用户共猜测的次数、实际价格。
针对上述要求,主要设计思路如下:通过环境变量 RANDOM 可获得一个小于 216 的随机整数,计算其与 1000 的余数即可获得 0~999 的随机价格;反复猜测操作可以通过以 true 作为测试条件的 while 循环实现,当用户猜中实际价格时终止循环;判断猜测价格与实际价格的过程采用 if 语句实现,嵌套在 while 循环体内;使用变量来记录猜测次数。

#!/bin/bash
PRICE=$(expr $RANDOM % 1000)
TIMES=0
echo "商品实际价格范围为 0-999,猜猜看是多少?" 
while true
do
   read -p "请输入你猜测的价格数目:" INT 
   let TIMES++
   if [ $INT -eq $PRICE ] ; then
       echo "恭喜你答对了,实际价格是 $PRICE" 
       echo "你总共猜测了 $TIMES 次"
       exit 0
  elif [ $INT -gt $PRICE ] ; then 
       echo "太高了!"
  else
       echo "太低了!"
       fi 
done
[root@localhost ~]# chmod +x pricegame.sh

测试并确认 pricegame.sh 脚本的执行结果如下所示:

[root@localhost ~]# ./pricegame.sh
商品实际价格范围为 0-999,猜猜看是多少?
请输入你猜测的价格数目:500 
太高了!
请输入你猜测的价格数目:250 
太低了!
请输入你猜测的价格数目:375 
太高了!
请输入你猜测的价格数目:280 
太高了!
请输入你猜测的价格数目:265 
太高了!
请输入你猜测的价格数目:253 
恭喜你答对了,实际价格是 253
你总共猜测了 6 次

三、使用 until 循环语句

until 循环与 while 循环类似,while 循环能实现的脚本 until 同样也可以实现,但区别是
while 循环在条件为真是继续执行循环,而 until 则是在条件为假时执行循环。

1、until 语句的结构

在这里插入图片描述
ntil 语句的执行流程:首先判断 until 后的条件测试操作结果,如果条件不成立,则执行 do…done 循环体中的命令序列;返回 until 后再次判断条件测试结果,如果条件仍然不成立,则继续执行循环体;再次返回到 until 后,判断条件测试结果……如此循环,直到 until 后的条件测试结果成立为止,最后跳转到 done 语句,表示结束循环,如图所示:
在这里插入图片描述

2、until 语句应用示例

(1)计算 1~50 的和

在一些科学计算领域,经常会用到各种数的计算,自然数的求和操作是最简单的。本例中计算从 1 到 50 的和,从 1 开始相加,采用循环的方式,每次循环后加 1,将得到的值加入计算的和中,数字运算采用的是 let 方式,直到加到 50 为止,具体的操作参考如下

[root@localhost ~]# vim sum1to50_until_v1.sh
#!/bin/bash 
i=0;s=0
until [ $i -eq 50 ] 
do
   let "i=$i+1";let "s=$s+$i" 
done
echo 'sum(1..50)='$s
[root@localhost ~]# chmod +x sum1to50_until_v1.sh 
[root@localhost ~]# ./sum1to50_until_v1.sh 
sum(1..50)=1275

上述代码中,在 i 的值小于 50 之前,每次循环 i 的值加 1,并且求出 s 的值。

(2)为指定用户发送在线消息

公司内部有一台 Linux 测试服务器,开发、测试、运维都在使用自己的账号连接登录到服务器上。当业务增加不能满足使用需求时,运维决定给服务器增加内存配置,要通知开发和测试人员保存数据退出,之后再关机升级内存,以应对业务的增加。
针对上面的需求,可编写一个名字为 until-user_online_to_write.sh 的 Shell 脚本,用于给已登录用户发送消息,对用户进行检测,必须是系统内用户并且处于登录状态,通过 Linux 的 write 命令来发送消息,具体的脚本如下所示:

[root@localhost ~]# vim until-user_online_to_write.sh
#!/bin/bash username=$1
if [ $# -lt 1 ]; then	//对脚本参数个数不满足条件的进行处理
    echo "Usage:`basename $0` <username> [<message>]"
    exit 1
fi
if grep "^$username:" /etc/passwd > /dev/null;then :	//判断第一个参数是否为系统用户
else
    echo "$username is not a user on this system." 
    exit 2
fi
until who|grep "$username" > /dev/null;do	//接收信息的用户必须为登录在线用户
    echo "$username is not logged on."
    sleep 600
done
shift;msg=$*	//shift 去除第一个参数;$*为所有参数的值
[[ "X"$msg == "X" ]] && msg="Are you ready ? $username"	//判断$msg 是否为空,为空则赋值
echo "$msg" | write $username	//通过 write 命令给用户发送消息
[root@localhost ~]# chmod +x until-user_online_to_write.sh
[root@localhost ~]# ./until-user_online_to_write.sh root	//发给root 自己,消息为空
Message from root@localhost on pts/0 at 16:23 ... 
Are you ready ? root
EOF
[root@localhost ~]# ./until-user_online_to_write.sh root hello       
                   //发给 root 自己,消息为“hello” 
Message from root@localhost on pts/0 at 16:23 ...
hello 
EOF
[root@localhost ~]# ./until-user_online_to_write.sh jerry hello
                   //发给用户 jerry,消息为“hello” 
[root@localhost ~]# ./until-user_online_to_write.sh jerry
                   //发给用户 jerry,消息为空

通过 write 方式发送消息的目标用户,必须是在线用户,处于自己的登录终端。执行此脚本时,将消息发给 jerry 用户,其结果在 jerry 登录的终端显示内容如下所示:

[jerry@localhost ~]$
Message from root@localhost on pts/0 at 17:15 ... 
Hello
EOF
Message from root@localhost on pts/0 at 17:16 ... 
Are you ready ?,jerry
EOF

四、Shell函数

1、Shell函数的用法

Shell 函数可用于存放一系列的指令。在 Shell 脚本执行的过程中,函数被置于内存中, 每次调用函数时不需要从硬盘读取,因此运行的速度比较快。在 Shell 编程中函数并非是必须的元素,但使用函数可以对程序进行更好的组织。将一些相对独立的代码变成函数,可以提高程序可读性与重用性,避免编写大量重复代码。
Shell 函数定义的方法如下所示:
在这里插入图片描述

  • “function”关键字表示定义一个函数,可以省略;
  • “{”符号表示函数执行命令的入口,该符号可以与函数名同行也可以在函数名下一行 的句首;
  • “}”符号表示函数体结束,两个大括号之间{ }是函数体;
  • “命令序列”部分可以是任意的 Shell 命令,也可以调用其他函数;
  • “return”表示退出函数返回一个退出值,通过返回值判断执行是否成功,也可以使用 exit 终止整个 Shell 脚本。

Shell 函数调用的方法:

函数名 [参数 1] [参数 2]

(1)两个数求和

使用 Shell 脚本实现两个数相加求和,通过定义函数的方式来完成。sum 函数内部通过read 命令接收用户分别输入的两个数,然后做加法运算,最后通过调用函数的方式来输出两个数的和。

[root@localhost ~]# vim sum.sh
#!/bin/bash 
sum(){
    
    
    read -p "请输入第一个数:" NUM1 
    read -p "请输入第二个数:" NUM2
    echo “你输入的两个数为: $NUM1$NUM2.” SUM=$(( NUM1+$NUM2))
    echo “两个数的和为: $SUM}
sum
[root@localhost ~]# chmod +x sum.sh
[root@localhost ~]# ./sum.sh
请输入第一个数:2 
请输入第二个数:3
“你输入的两个数为: 2 和 3.”
“两个数的和为: 5”

(2)编写用户自定义函数

CentOS 系统由 6 版本升级到 7 版本之后,其启动服务的方式发生了很大变化。在生产
环境中还有很大一部分的企业在使用 6 系列,为了兼容 6 和 7,要求写一函数自动判断系统型号,根据型号执行对应的服务管理程序,并且设置开机生效。

[root@localhost ~]# vim function
servicectl_usage () {
    
    
    echo "Usage:servicectl <service-name> <start|stop|restart|reload|status>" 
    return 1
}

chk_centos_ver () {
    
    
    grep "CentOS.*release 7." /etc/centos-release &> /dev/null && echo "7" 
    grep "CentOS.*release 6." /etc/centos-release &> /dev/null && echo "6" 
    grep "CentOS.*release 5." /etc/centos-release &> /dev/null && echo "5"
}

servicectl () {
    
    
    [[ -z $1 || -z $2 ]] && servicectl_usage
    [ $(chk_centos_ver)=="7" ] && systemctl $2 ${1}.service || service $1 $2
}
[root@localhost ~]# chmod +x function
[root@localhost ~]# source function
[root@localhost ~]# echo 'source /root/function' >> ~/.bashrc
[root@localhost ~]# servicectl vsftpd status
●vsftpd.service - Vsftpd ftp daemon
  Loaded: loaded (/usr/lib/systemd/system/vsftpd.service;  disabled; vendor preset: disabled) 
  Active: active(running) since Thu 2019-11-14 13:49:55 CST; 42s ago
  Process:	2369	ExecStart=/usr/sbin/vsftpd	/etc/vsftpd/vsftpd.conf	(code=exited, status=0/SUCCESS)
  Main PID: 2370 (vsftpd)
    CGroup: /system.slice/vsftpd.service
           └─2370 /usr/sbin/vsftpd      /etc/vsftpd/vsftpd.conf
Nov 14 13:49:55 virgon systemd[1]: Starting Vsftpd ftp daemon... 
Nov 14 13:49:55 virgon systemd[1]: Started Vsftpd ftp daemon.

CentOS 系统文件/etc/centos-release 记录着系统的版本号,通过该文件来判断CentOS
是属于 6 还是 7 系列。然后对 servicectl 这个函数的参数进行判断,如果参数为空,则执行servicectl_usage 函数并给出提示,最后在根据系统是 6 还是 7,分别执行对应的服务管理程序对程序进行启动、关闭等操作。

2、函数变量的作用范围

在 Shell 脚本中函数的执行并不会开启一个新的子 Shell,而是仅在当前定义的 Shell 环境中有效。如果 Shell 脚本中的变量没有经过特殊设定,默认在整个脚本中都是有效的。在编写脚本时,有时需要将变量的值限定在函数内部,可以通过内置命令 local 来实现。函数内部变量的使用,可以避免函数内外同时出现同名变量对脚本结果的影响。local 命令的使用如下所示:
在这里插入图片描述
上述脚本中,myfun 函数内部使用了 local 命令设置变量 i,其作用是将变量 i 限定在函数内部。myfun 函数外部同样定义了变量 i,内部变量 i 和全局变量 i 互不影响。脚本执行时先调用了函数 myfun,函数内部变量 i 为 8,所以输出结果是 8。调用完函数之后,给变量i 赋值为 9,再打印外部变量 i,所以又输出 9。

3、函数的参数

函数的参数的用法如下:

函数名称 参数 1 参数 2 参数 3 ......

在使用函数参数时,函数名称在前参数在后,函数名和参数之间用空格分隔,可以有多个参数,参数使用$1、$2、 3 … … 的 方 式 表 示 。 以 此 类 推 , 从 第 10 个 参 数 开 始 , 调 用 方 法 为 3……的方式表示。以此类推,从第 10 个参数开始,调用方法为 310{10},不加大括号无法调用成功。下面是函数参数的一个简单应用:
在这里插入图片描述
上述脚本接收两个参数,第一个参数是写日志的目标文件,第二个参数是日志信息,整 个脚本实现将日志信息写入目标文件内的目的。

4、递归函数

Shell 也可以实现递归函数,就是可以调用自己本身的函数。在 Linux 系统上编写 Shell 脚本的时候,经常需要递归遍历系统的目录,列出目录下的文件和目录,逐层递归列出,并 对这些层级关系进行展示。具体的实现过程如下所示:

[root@localhost ~]# vim fun_recursion.sh
function list_files()
{
    
    
    for f in `ls $1`; 
    do
        if [ -d "$1/$f" ]; then 
            echo "$2$f"
            list_files "$1/$f" "	$2"
        else
            echo "$2$f" 
        fi
    done
}
list_files "/var/log" ""
[root@localhost ~]# chmod +x fun_recursion.sh
[root@localhost ~]# ./fun_recursion.sh

函数 list_files 的第一个参数是列举的目录名,第二个参数是调整的空间。执行脚本后, 其结果显示如下:
在这里插入图片描述

五、Shell数组

1、应用场景包括

在 Shell 脚本中,数组是一种常见的数据结构,主要的应用场景包括:获取数组长度、获取元素长度、遍历元素、元素切片、元素替换、元素删除等等。Shell 中的数组与 Java、C、Python 不同,只有一维数组,没有二维数组。数组元素的大小与限制,也不需要事先定义。Shell 数组用括号()来表示,元素用空格分隔,元素的下标与大部分编程语言类似从 0 开始。
在这里插入图片描述

2、数组定义方法

数组常用定义方法包括以下几种:

  • 方法一:
    数组名=(value0 value1 value2 …)
  • 方法二:
    数组名=([0]=value [1]=value [2]=value …)
  • 方法三:
    列表名=”value0 value1 value2 …”
    数组名=($列表名)
  • 方法四:
    数组名[0]=”value”
    数组名[1]=”value”
    数组名[2]=”value”

3、数组的基本使用方法

(1)获取数组长度

[root@localhost ~]# arr_number=(1 2 3 4 5) 
[root@localhost ~]# arr_length=${#arr_number[*]} 
[root@localhost ~]# echo $arr_length
5
[root@localhost ~]# arr_length_1=${#arr_number[@]}

[root@localhost ~]# echo $arr_length_1
5

(2)读取某下标赋值

[root@localhost ~]# arr_index2=${arr_number[2]}	//第三个元素
[root@localhost ~]# echo $arr_index2
3

(3)数组遍历

[root@localhost ~]# vim array_traverse.sh
#!/bin/bash 
arr_number=(1 2 3 4 5) 
for v in ${arr_number[@]} 
do
  echo $v 
done
[root@localhost ~]# chmod +x array_traverse.sh
[root@localhost ~]# ./array_traverse.sh
1
2
3
4
5

(4)数组切片

[root@centos-7 ~]# arr=(1 2 3 4 5)
[root@centos-7 ~]# echo ${arr[@]}	//输出整个数组
1 2 3 4 5
[root@centos-7 ~]# echo ${arr[@]:0:2}	//${数组名[@或*]:起始位置:长度} 
1 2
[root@centos-7 ~]# echo ${arr[@]:2:3}
3 4 5

将数组切片之后,返回的是字符串,以空格作为分隔符。
(5)数组替换

[root@centos-7 ~]# arr=(1 2 3 4 5)
[root@centos-7 ~]# echo ${arr[@]/4/66}	//${数组名[@或*]/查找字符/替换字符} 
1 2 3 66 5
[root@centos-7 ~]# echo ${arr[@]}	//并不会替换数组原有内容
1 2 3 4 5
[root@centos-7 ~]# arr=(${arr[@]/4/66})	//要实现改变原有数组,可通过重新赋值实现
[root@centos-7 ~]# echo ${arr[@]}
1 2 3 66 5

(6)数组删除

[root@centos-7 ~]# arr=(1 2 3 4 5)
[root@centos-7 ~]# unset arr	//删除数组
[root@centos-7 ~]# echo ${arr[*]}

[root@centos-7 ~]# arr=(1 2 3 4 5)
[root@centos-7 ~]# unset arr[2]	//删除第三个元素
[root@centos-7 ~]# echo ${arr[*]}
1 2 4 5

4、数组包括的数据类型

在这里插入图片描述

六、Shell 脚本调试

在 Shell 脚本开发中,经常碰到一些规范方面的问题,例如忘了使用引号或在 if 语句末尾处忘记加 fi 结束。要注意把复杂的脚本简单化,要思路清晰,并且分段实现。当执行脚本时出现错误后,不要只看那些提示的错误行,而是要观察整个相关的代码段。
为避免编写的脚本出错,除了在编写脚本时注意书写规范,排除语法错误,更重要的是 利用调试脚本工具来调试脚本。echo 命令是最有用的调试脚本工具之一,一般在可能出现问题的脚本中加入 echo 命令,采用的是分段排查的方式。
除了 echo 命令之外,bash Shell 也有相应参数可以调试脚本。使用 bash 命令参数调试,命令的语法为:

sh [-nvx] 脚本名

常用参数的具体含义为:

  • -n:不会执行该脚本,仅查询脚本语法是否有问题,如果没有语法问题就不显示任 何内容,如果有问题会提示报错。
  • -v:在执行脚本时,先将脚本的内容输出到屏幕上然后执行脚本,如果有错误,也 会给出错误提示。
  • -x:将执行的脚本内容输出到屏幕上,这个是对调试很有用的参数。

当脚本文件较长时,可以使用 set 命令指定调试一段脚本。

#!/bin/bash
set -x	//开启调试模式
read -p "请输入您的分数(0-100):" GRADE
if [ $GRADE -ge 85 ] && [ $GRADE -le 100 ]  then
echo "$GRADE 分!优秀"
set +x	//关闭调试模式
elif [ $GRADE -ge 70 ] && [ $GRADE -le 84 ] 
then
    echo "$GRADE 分,合格"
else
    echo "$GRADE 分?不合格"
fi

猜你喜欢

转载自blog.csdn.net/ZG_66/article/details/107983476
今日推荐