if、for、while、case 这 4 种流程控制语句来学习编写难度更大、功能更强的 Shell 脚本。
双引号("):双引号用于创建一个包含文本的字符串。双引号内的文本可以包含变量和特殊字符的扩展,例如转义字符(\),命令替换(command或$(command)),以及变量的引用($variable)。
name="John"
echo "My name is $name."
输出将是:My name is John.
单引号('):单引号用于创建一个包含文本的字符串,但是它会将内部的所有字符视为字面量,不进行任何扩展或替换。单引号保留了字符串中的原义,包括特殊字符、变量和命令替换等都不会被执行或扩展。
name="John"
echo 'My name is $name.'
输出将是:My name is $name.
可以看到,单引号内的变量$name并没有被替换为实际的值。
if 条件测试语句
下面使用单分支的 if 条件语句来判断/media/cdrom 目录是否存在,若不存在就创建这个目录,反之则结束条件判断和整个 Shell 脚本的执行。
vim mkcdrom.sh
#! /bin/bash
DIR = "/media/cdrom"
if [ ! -d $DIR ]
then
mkdir -p $DIR
fi
由于第 5 章才讲解用户身份与权限,因此这里继续用“bash 脚本名称”的方式来执行脚本。在正常情况下,顺利执行完脚本文件后没有任何输出信息,但是可以使用 ls 命令验证/media/cdrom 目录是否已经成功创建:
bash mkcdrom.sh
ls -ld /media/cdrom
if 条件语句的双分支结构由 if、then、else、fi 关键词组成,它进行一次条件匹配判断,如果与条件匹配,则去执行相应的预设命令;反之则去执行不匹配时的预设命令,相当于口语的“如果……那么……或者……那么……”。if 条件语句的双分支结构也是一种很简单的判断结构,语法格式如图 所示
下面使用双分支的 if 条件语句来验证某台主机是否在线,然后根据返回值的结果,要么显示主机在线信息,要么显示主机不在线信息。这里的脚本主要使用 ping 命令来测试与对方主机的网络连通性,而 Linux 系统中的 ping 命令不像 Windows 一样尝试 4 次就结束,因此为了避免用户等待时间过长,需要通过-c 参数来规定尝试的次数,并使用-i 参数定义每个数据包的发送间隔,以及使用-W 参数定义等待超时时间。
vim chkhost.sh
#! /bin/bash
ping -c 3 -i 0.2 -W 3 $1 &> /dev/null
if [ $? -eq 0 ]
then
echo "Host $1 is On-line"
else
echo "Host $1 is Off-line"
fi
因此可以使用整数比较运算符来判断$?变量是否为 0,从而获知那条语句的最终判断情况。这里的服务器 IP 地址为 192.168.10.10,我们来验证一下脚本的效果:
bash chkhost.sh 192.168.10.10
bash chkhost.sh 192.168.10.20
if 条件语句的多分支结构由 if、then、else、elif、fi 关键词组成,它进行多次条件匹配判断,这多次判断中的任何一项在匹配成功后都会执行相应的预设命令,相当于口语的“如果……那么……如果……那么……”。if 条件语句的多分支结构是工作中最常使用的一种条件判断结构,尽管相对复杂但是更加灵活,语法格式如图所示。
下面使用多分支的 if 条件语句来判断用户输入的分数在哪个成绩区间内,然后输出如Excellent、Pass、Fail 等提示信息。在 Linux 系统中,read 是用来读取用户输入信息的命令,能够把接收到的用户输入信息赋值给后面的指定变量,-p 参数用于向用户显示一些提示信息。
在下面的脚本示例中,只有当用户输入的分数大于等于 85 分且小于等于 100 分时,才输出 Excellent 字样;若分数不满足该条件(即匹配不成功),则继续判断分数是否大于等于 70分且小于等于 84 分,如果是,则输出 Pass 字样;若两次都落空(即两次的匹配操作都失败了),则输出 Fail 字样:
vim chkscore.sh
#!/bin/bash
read -p "Enter your score (0-100):" GRADE
if [ $GRADE -ge 85 ] && [ $GRADE -le 100 ] ; then
echo "$GRADE is Excellent"
elif [ $GRADE -ge 70 ] && [ $GRADE -le 84 ] ; then
echo "$FREADE is pass"
else
echo "$GRADE is Fail"
fi
bash chkscore.sh
88
bash chkscore.sh
80
下面执行该脚本。当用户输入的分数分别为 30 和 200 时,其结果如下:
bash chkscore.sh
30
bash chkscore.sh
200
for 条件循环语句
for 循环语句允许脚本一次性读取多个信息,然后逐一对信息进行操作处理。当要处理的数据有范围时,使用 for 循环语句就再适合不过了。for 循环语句的语法格式如图所示。
下面使用 for 循环语句从列表文件中读取多个用户名,然后为其逐一创建用户账户并设置密码。首先创建用户名称的列表文件 users.txt,每个用户名称单独一行。读者可以自行决定具体的用户名称和个数:
vim users.txt
andy
barry
carl
duck
eric
george
接下来编写 Shell 脚本 addusers.sh。在脚本中使用 read 命令读取用户输入的密码值,然后赋值给 PASSWD 变量,并通过-p 参数向用户显示一段提示信息,告诉用户正在输入的内容即将作为账户密码。在执行该脚本后,会自动使用从列表文件 users.txt 中获取到所有的用户名称,然后逐一使用“id 用户名”命令查看用户的信息,并使用$?判断这条命令是否执行成功,也就是判断该用户是否已经存在。
vim addusers.sh
#!/bin/bash
read -p "Enter The Users Password :" PASSWD
for UNAME in `cat users.txt`
do
id $UNAME &> /dev/null
if [ $? -eq 0 ]
then
echo "UNAME, Already exists"
else
useradd $UNAME &> /dev/null
echo "$PASSWD" | passwd --stdin $UNAME &> /dev/null
echo "$UNAME, Create success"
fi
done
/dev/null 是一个被称作 Linux 黑洞的文件,把输出信息重定向到这个文件等同于删除数据(类似于没有回收功能的垃圾箱),可以让用户的屏幕窗口保持简洁。
在 Linux 系统中,/etc/passwd 是用来保存用户账户信息的文件。如果想确认这个脚本是否成功创建了用户账户,可以打开这个文件,看其中是否有这些新创建的用户信息。
bash addusers.sh
tail -6 /etc/passwd
尝试让脚本从文本中自动读取主机列表,然后自动逐个测试这些主机是否在线。
首先创建一个主机列表文件 ipaddrs.txt:
vim ipaddrs.txt
vim CheckHosts.sh
#!/bin/bash
HLTST=$(cat~/ipaddrs.txt)
for IP in $HLIST
do
ping -c 3 -i 0.2 -W 3 $IP &> /dev/null
if [ $? -eq 0 ]
then
echo "Host $IP is On-line"
else
echo "Host $IP is Off-line"
fi
done
./CheckHosts.sh