简单介绍一下Shell编程中的控制流程语句,包括if
条件语句、for
循环、while
循环、case
选择语句、select
选择语句、函数和数组的基本用法。
3.1 if
条件语句
3.1.1 单/双分支 if
语句
-
单分支语句: 用于在满足某个条件时执行特定语句。
if (条件表达式); then 语句1 fi
-
双分支语句: 包含
else
分支,当条件不满足时执行另一组语句。if (表达式); then 语句1 else 语句2 fi
-
多支条件语句: 使用
elif
进行多条件判断。if (表达式); then 语句1 elif (表达式); then 语句2 elif (表达式); then 语句3 fi
3.1.2 常见判断逻辑运算符
-f
: 判断文件是否存在-d
: 判断目录是否存在-eq
: 整型比较,等于-ne
: 整型比较,不等于-lt
: 整型比较,小于-gt
: 整型比较,大于-le
: 整型比较,小于或等于-ge
: 整型比较,大于或等于-a
: 逻辑与-o
: 逻辑或-z
: 判断字符串是否为空-x
: 判断文件是否具有可执行权限
3.1.3 判断 crond
进程是否在运行的案例
#!/bin/bash
# this is check crond
name=crond
num=$(ps -ef | grep $name | grep -vc grep)
if [ $num -eq 1 ]; then
echo "$num is running!"
else
echo "$num is not running!"
fi
- 该脚本通过
ps -ef
命令获取进程列表,并使用grep
查找crond
进程的数量。如果找到数量为1,则表示该进程正在运行。grep -vc grep
忽略grep
这个进程。
3.1.4 判断系统目录是否存在的案例
#!/bin/bash
# this is check directory
if [ ! -d /data/rivers -a ! -d /tmp/rivers ]; then
mkdir -p /data/rivers
fi
- 该脚本判断两个目录是否都不存在,如果不存在则创建
/data/rivers
目录。
3.1.5 多个条件判断学生分数等级的案例
#!/bin/bash
# this check grade shell
grade=$1
if [ $grade -gt 90 ]; then
echo "It's very good!"
elif [ $grade -gt 70 ]; then
echo "It's good!"
elif [ $grade -ge 60 ]; then
echo "pass"
else
echo "no pass"
fi
- grade=$1 获取第一个变量
- 该脚本根据输入的分数判断学生的等级,分数范围对应不同的输出。
3.2 for
循环语句介绍
# 格式:for name [ in [ word … ] ] ; do list ; done
for 变量名 in 取值列表; do
语句1
done
for
循环用于遍历一个列表中的每个值。
3.2.1 检查同一局域网多台主机是否存活的案例
#!/bin/bash
# check hosts is on/off
Network=$1
for Host in $(seq 1 254); do
ping -c 1 $Network.$Host > /dev/null && result=0 || result=1
if [ "$result" == 0 ]; then
echo -e "\033[32mNetwork.$Host is up\033[0m"
echo "$Network.$Host" >> /tmp/up.txt
else
echo -e "\033[31mNetwork.$Network.$Host is down\033[0m"
echo "$Network.$Host" >> /tmp/down.txt
fi
done
-
ping -c 1 Network.Host:这部分命令对 Network.Host 这个主机进行一次 ICMP ping 测试。-c 1 参数指定只发送一个请求。
-
其中, > /dev/null:这是输出重定向,将 ping 命令的输出(成功信息和错误信息)丢弃,不显示在终端上。/dev/null 是一个特殊的文件,写入其内容会被丢弃。
-
&& 和 ||:这是 Bash 中的逻辑运算符。
- &&:如果前面的命令(ping)成功(返回值为 0),则执行 result=0。
- ||:如果前面的命令失败(返回值非 0),则执行 result=1。
-
该脚本使用
ping
命令检查从Network.1
到Network.254
的每台主机是否存活,并记录结果。
3.3 while
循环语句介绍
# while (表达式)
while [ condition ]; do
语句1
done
while
循环在条件为真时执行语句,适用于需要重复执行的任务。
3.3.1 While循环求1-100的总和的案例
#!/bin/bash
j=0
i=1
while ((i <= 100)); do
j=$((i + j))
((i++))
done
echo $j
- 该脚本计算1到100的总和。
3.4 case
选择语句介绍
# Case选择语句
case 模式名 in
模式1)
命令
;;
模式2)
命令
;;
*)
不符合以上模式执行的命令
esac
case
语句用于多个条件的选择,类似于if-elif
结构。
3.4.1 使用 case
编写一个 httpd
服务启动脚本的案例
#!/bin/bash
# check http server start|stop|status
while true; do
echo -e "选择: start, stop, status, quit"
read -p "请输入你的选择: " char
case $char in
start)
systemctl start httpd && echo "httpd服务已经开启" || echo "开启失败"
;;
stop)
systemctl stop httpd && echo "httpd服务已经关闭" || echo "关闭失败"
;;
status)
systemctl status httpd
;;
quit)
exit
;;
*)
echo "输入错误,请重新输入!"
;;
esac
done
- read: 这是一个用于从标准输入读取用户输入的命令。
- -p: 这个选项允许你在读取输入之前打印一个提示符。这里,它会打印“请输入你的选择: ”。
- char: 这是变量名,read 命令将用户输入的值存储在这个变量中。用户输入的内容将被赋值给 char。
- 该脚本提供一个菜单,可以启动、停止、查看状态或退出HTTP服务。
3.5 select
选择语句介绍
# select 语句
select var in (选项); do
命令
done
select
用于生成一个菜单供用户选择,常用于交互式脚本。
3.5.1 使用 select
打印 lnmp
菜单栏的案例
#!/bin/bash
PS3="请选择你要安装的菜单:"
select i in http php mysql quit; do
case $i in
http)
echo -e "\033[31m测试Httpd\033[0m"
;;
php)
echo -e "\033[32m测试PHP\033[0m"
;;
mysql)
echo -e "\033[33m测试MySQL\033[0m"
;;
quit)
echo -e "\033[32m退出系统\033[0m"
exit
;;
*)
echo "输入错误,请重新输入!"
;;
esac
done
- 该脚本创建一个简单的菜单供用户选择不同的服务。
-
#!/bin/bash
: 指定脚本使用的解释器为 Bash。 -
PS3="请选择你要安装的菜单:"
: 设置select
命令的提示符,这里是当用户需要输入选择时显示的提示信息。 -
select i in http php mysql quit; do
:- 使用
select
命令创建一个菜单,i
将存储用户的选择。 - 菜单选项包括
http
,php
,mysql
和quit
。 - 用户输入选项后,程序进入
do
和done
之间的循环。
- 使用
-
case $i in
: 开始一个case
语句,根据用户选择的值(存储在i
中)执行相应的操作。
当用户运行这个脚本时,会看到如下输出的菜单:
请选择你要安装的菜单:
1) http
2) php
3) mysql
4) quit
# 这里会显示菜单选项,并等待用户输入一个数字
- 用户输入一个数字(例如
1
,2
,3
,或4
)后,程序将根据选择打印相应的消息。 - 如果用户输入无效选项(即不是 1 到 4 之间的数字),程序将提示用户
输入错误,请重新输入!
,然后再次显示菜单。
3.6 Shell 函数和数组编程
3.6.1. Shell 函数
定义函数
在 Shell 中,函数是一组命令的集合,可以在脚本中多次调用,避免重复代码。函数的基本定义如下:
func() {
command1
command2
...
}
函数调用
要调用定义的函数,只需使用函数名即可,不需要括号。例如:
func
示例代码
#!/bin/bash
func() {
VAR=$((1 + 1))
echo "This is a function."
return $VAR
}
func
echo $? # 输出函数的返回值,即2
- 这里
echo $?
用于获取上一个命令(即函数)的返回值。在这个示例中,VAR
的值是2
,所以输出为2
。
3.6.2. Shell 数组
数组在 Shell 中用于存储多个值,基本定义格式为:
array=(element1 element2 element3 ...)
数组定义方法
-
方法 1:直接初始化
array=(a b c)
-
方法 2:通过下标添加元素
array[0]=a array[1]=b array[2]=c
-
方法 3:命令输出作为数组元素
array=($(command))
command
: 这是你要执行的命令。命令的输出将被捕获并存储到数组中。()
: 用于将命令的输出分配给数组。注意要使用小括号,且需要在赋值符号=
的两边没有空格。
示例1:简单命令
#!/bin/bash
# 使用 `ls` 命令获取当前目录下的文件和文件夹
files=($(ls))
# 遍历数组并打印每个文件名
echo "当前目录下的文件和文件夹有:"
for file in "${files[@]}"; do
echo "$file"
done
-
files=($(ls))
:- 执行
ls
命令,获取当前目录下的所有文件和文件夹。 - 通过
$(...)
捕获命令的输出,并将其分配给数组files
。此时,files
数组中每个元素对应ls
命令输出中的一个文件或文件夹名。
- 执行
-
for file in "${files[@]}"; do ... done
:- 使用
for
循环遍历数组files
中的每个元素。 "${files[@]}"
是访问数组的语法,确保每个元素都被正确处理,特别是在元素中包含空格的情况下。
- 使用
如果在一个包含文件的目录下运行这个脚本,输出可能是:
当前目录下的文件和文件夹有:
file1.txt
file2.txt
directory1
directory2
- 空格处理: 当命令的输出包含空格时,使用
"${array[@]}"
可以避免将其拆分成多个元素。例如,如果输出中有文件名为my file.txt
,使用"$file"
而不是$file
可以确保整个文件名被视为一个字符串。 - 命令输出的格式: 如果命令输出的结果很复杂,比如多行或多列数据,可能需要进一步处理以获取所需的数组元素。
示例2:使用 ps
命令
下面是一个更复杂的示例,使用 ps
命令获取当前运行的进程并将其存储在数组中:
#!/bin/bash
# 使用 `ps` 命令获取当前运行的进程
processes=($(ps -eo cmd))
# 输出每个进程的命令
echo "当前运行的进程:"
for process in "${processes[@]}"; do
echo "$process"
done
ps -eo cmd
: 此命令列出所有当前运行的进程的命令部分。processes=($(ps -eo cmd))
: 将这些命令的输出存储到processes
数组中。- 输出: 遍历数组并打印每个进程的命令。
3.6.3. 安装 Apache 的函数示例
编写代码
#!/bin/bash
# auto install apache
FILES=httpd-2.2.31.tar.bz2
FILES_DIR=httpd-2.2.31
URL=http://mirrors.cnnic.cn/apache/httpd/
PREFIX=/usr/local/apache2/
function Apache_install() {
# Install httpd web server
if [[ "$1" -eq "1" ]]; then
wget -c $URL$FILES && tar -jxvf $FILES && cd $FILES_DIR && ./configure
if [ $? -eq 0 ]; then
make && make install
echo -e "\033[32m--------------------------------------------"
echo -e "\033[32mThe $FILES_DIR Server Install Success !\033[0m"
else
echo -e "\033[31mThe $FILES_DIR Make or Make install ERROR, Please check!\033[0m"
exit 0
fi
fi
}
Apache_install 1 # 调用函数,传参为1
代码逐行解释
-
wget -c $URL$FILES
wget
是一个下载工具,-c
选项表示如果下载中断,可以继续下载。$URL
和$FILES
是提前定义好的变量,分别代表下载的 URL 和文件名。- 这个命令用于从指定的 URL 下载名为
$FILES
的压缩包。
-
&& tar -jxvf $FILES
&&
用于连接多条命令,表示前一个命令成功执行后才执行后面的命令。tar -jxvf
用于解压缩bzip2
压缩格式的tar
包。-j
表示使用bzip2
解压缩。-x
表示解压缩文件。-v
表示详细显示解压缩过程。-f
表示后面跟的是要解压的文件名。
$FILES
是下载的压缩包名称。
-
&& cd $FILES_DIR
cd
是改变目录的命令,用于进入解压后的文件夹。$FILES_DIR
是提前定义的变量,代表解压缩后得到的目录名。
-
&& ./configure
- 执行
./configure
命令进行预编译配置,生成Makefile
。 ./configure
是httpd
安装的第一步,检查系统环境和依赖项,以便生成适合系统的编译配置。
- 执行
-
if [ $? -eq 0 ]; then
$?
是上一条命令执行的返回状态码。0
表示上一条命令成功执行。- 这里判断
./configure
是否执行成功,如果成功则继续执行下一步。
-
make && make install
make
用于根据Makefile
编译源代码。make install
用于将编译后的程序安装到指定的位置(一般是系统路径中)。- 这里用
&&
连接,表示只有make
成功后才执行make install
。
-
echo -e "\033[32m--------------------------------------------"
echo -e
用于打印带有转义字符的字符串。\033[32m
是 ANSI 转义码,用于将输出文本颜色设置为绿色。- 打印出分隔线,方便阅读输出信息。
-
echo -e "\033[32mThe $FILES_DIR Server Install Success !\033[0m"
- 打印安装成功的提示信息。
\033[0m
是 ANSI 转义码,用于重置文本颜色。- 如果安装成功,显示绿色文本提示
The $FILES_DIR Server Install Success !
。
-
else
- 如果
./configure
或make
的执行失败,进入else
分支。
- 如果
-
echo -e "\033[31mThe $FILES_DIR Make or Make install ERROR, Please check!\033[0m"
- 打印错误提示信息。
\033[31m
将文本颜色设置为红色,提示用户安装过程中出现了错误。\033[0m
重置文本颜色。
-
exit 0
- 终止脚本的执行,返回状态码
0
(一般表示正常退出)。 - 这里用于在出现错误时停止脚本继续执行。
- 终止脚本的执行,返回状态码
3.6.4. 遍历数组元素
方法 1:使用下标遍历
#!/bin/bash
IP=(10.0.0.1 10.0.0.2 10.0.0.3)
for ((i=0; i<${#IP[*]}; i++)); do
echo ${IP[i]}
done
${#IP[*]}
获取数组 IP 的长度,即数组中元素的数量。- 用于获取数组的长度。
IP
是数组变量。${#IP[*]}
获取数组 IP 中的所有元素个数。[*]
表示获取数组的所有元素,可以换成 [@],在获取数组长度时两者效果相同。
方法 2:使用 for
循环遍历
#!/bin/bash
IP=(10.0.0.1 10.0.0.2 10.0.0.3)
for ip in "${IP[@]}"; do # 使用引号确保处理包含空格的元素
echo $ip
done
${IP[@]}
[@]
是用于获取数组 IP 中所有元素的一种方式。IP[@]
表示数组 IP 中的所有元素。${IP[@]}
会将数组的每个元素展开为独立的值。- 如果数组
IP
包含多个元素,这个语法会将它们全部传递给 for 循环。
数组遍历的输出
示例 3.6.4和3.6.5 的输出内容是
10.0.0.1
10.0.0.2
10.0.0.3