Linux Shell 编程:条件判断、循环与函数实战指南

在 Linux 运维和开发中,Shell 脚本是自动化任务的利器。无论你是初学者还是有一定经验的开发者,熟练掌握 test、条件判断、循环结构以及函数封装,都能让你的脚本更健壮、更高效。

  • 条件判断(如 ifcase)让脚本能够根据不同的系统状态或输入做出决策,避免硬编码逻辑,从而提升脚本的适应性和健壮性。
  • 循环结构(如 forwhileuntil)使你能够高效地处理重复性任务,例如批量处理文件或监控进程状态,极大地提升了自动化效率。
  • 函数封装将复杂的操作模块化,不仅提高了代码的复用性,还让脚本结构更清晰,便于调试和维护,是构建大型、健壮脚本的基石。本文将系统梳理这些核心知识点,并辅以大量示例,助你快速上手。

1. test 命令与条件测试

Shell 中的条件测试主要通过 test 命令或其等价形式 [ ] 来实现。此外,Bash 还提供了增强版 [[ ]],支持更多特性。

1.1 test[ ] 的基本用法

test 条件表达式
# 或
[ 条件表达式 ]   # 注意括号与表达式之间必须有空格

常用测试类型

  • 文件测试-f(普通文件)、-d(目录)、-e(存在)、-r(可读)、-w(可写)、-x(可执行) 等。
  • 字符串测试-z(空串)、-n(非空)、==(相等)、!=(不等)。
  • 数值测试-eq(等于)、-ne(不等)、-gt(大于)、-lt(小于)、-ge(≥)、-le(≤)。
  • 文件比较
> -nt  # test file1 -nt file2    测试file1是否比file2新
> -ot  # test file1 -ot file2    测试file1是否比file2旧
> -ef  # 测试file1和file2是否为同一个文件。
  • 多重条件判断
-a : test -r file -a -x file
-o : test -r file -o -x file
!  : test -r file ! -x file

示例

[ -f /etc/passwd ] && echo "文件存在"
[ "$USER" = "root" ] && echo "当前是 root"
[ $# -eq 0 ] && echo "没有传入参数"

1.2 [[ ]] 增强测试(Bash 特有)

[[ ]] 是 Bash 的关键字,比 [ ] 更智能:

  • 支持 &&|| 逻辑运算符([ ] 内必须用 -a-o)。
  • 支持正则表达式 =~
  • 支持通配符匹配(如 [[ $str == a* ]])。
  • 变量引用时可以不加双引号(但建议加)。

示例

[[ -z "$var" ]] && echo "var 为空"
[[ $num -gt 10 && $num -lt 20 ]] && echo "num 在 10~20 之间"
[[ $file == *.txt ]] && echo "是 txt 文件"

2.shell脚本

shell脚本可以简单的理解为批处理程序,使用shell脚本可以做如下一些功能:

   (1)自动化管理;
   (2)管理系统;
   (3)简单的入侵侦测功能;
   (4)连续指令单一化;
   (5)简单的资料处理。

shell脚本注意事项:

指令执行是从上到下、从左到右分析执行的;
指令、选项与参数间的多个空白会被忽略掉;
空白行也会被忽略的;
如果读取到一个enter,就尝试执行该行命令;
#可作为注释,#后面的文字都会被忽略。

shell脚本执行:

绝对路径下达执行:/home/zhang/shell.sh
相对路径下达执行:./shell.sh
将shell.sh放入PATH目录
以bash程式执行:bash shell.sh或sh shell.sh
注意:以上执行方式中,第1-3种方法需要给脚本赋予执行权限才能执行,第4种方式不需要执行权限。

SHELL语法:

  #!/bin/bash         #宣告这个shell脚本使用的shell名称
  程序说明             #程序功能,程序版本,作者等说明
  环境变量宣告          #PATH=/bin:/usr/local/bin..........
  主程序部分            #主要程序
  执行成果告知          #定义返回值 exit 0, exit n

注意:#!/bin/bash 称为Shebang,是 Linux/Unix 系统中脚本文件的第一行特殊标记,由 #! 开头,后接解释器的绝对路径,它的核心作用是告诉操作系统‌使用哪个解释器来执行该脚本‌。主程序部分是shell脚本功能的实现。这两个部分是shell脚本必须要有的部分,其他部分可选,但一般为了脚本规范及后期维护,程序说明、环境变量宣告也会添加在脚本中。
如下通过一个简单的脚本说明shell脚本结构:
在这里插入图片描述

3. 条件判断:if 语句

if 语句根据命令的退出状态码(0 为真,非 0 为假)来决定执行路径。

3.1 基本结构

if 条件; then
    命令
elif 条件; then
    命令
else
    命令
fi

3.2 实战示例

#!/bin/bash
read -p "请输入一个数字: " num

if [[ $num -eq 0 ]]; then
    echo "零"
elif [[ $num -gt 0 ]]; then
    echo "正数"
else
    echo "负数"
fi

注意if 后可以直接跟命令,而不只是 test[][[]],例如:

if grep -q "error" /var/log/messages; then
    echo "发现错误日志"
fi

4. 多分支选择:case 语句

当分支较多时,case 比嵌套 if-elif 更清晰。

4.1 语法

case 变量 in
    模式1)
        命令;;
    模式2)
        命令;;
    *)
        默认命令;;
esac

4.2 示例:服务管理脚本

#!/bin/bash
case $1 in
    start)
        echo "启动服务..."
        systemctl start myapp
        ;;
    stop)
        echo "停止服务..."
        systemctl stop myapp
        ;;
    restart)
        echo "重启服务..."
        systemctl restart myapp
        ;;
    status)
        systemctl status myapp
        ;;
    *)
        echo "用法: $0 {start|stop|restart|status}"
        exit 1
        ;;
esac

模式支持通配符(*?[])和竖线 | 表示或,例如 [yY]|[yY][eE][sS])


5. 循环结构

5.1 for 循环

for 用于遍历列表或数值范围。

遍历列表

for name in Alice Bob Charlie; do
    echo "Hello, $name"
done

# 遍历文件
for file in *.txt; do
    echo "Processing $file"
done

C 风格 for(Bash 支持):

for ((i=1; i<=5; i++)); do
    echo "第 $i 次迭代"
done

实用示例:批量重命名

for f in *.jpg; do
    mv "$f" "${f%.jpg}.png"
done

5.2 while 循环

当条件为真时持续执行。

语法

while 条件; do
    命令
done

示例:逐行读取文件

while IFS= read -r line; do
    echo "行内容: $line"
done < /etc/passwd

无限循环

while true; do
    echo "按 Ctrl+C 退出"
    sleep 1
done

5.3 until 循环

while 相反,当条件为假时执行,直到条件变为真。

语法

until 条件; do
    命令
done

示例:等待某个进程退出

until ! pgrep -x "myprocess" > /dev/null; do
    echo "进程仍在运行,等待中..."
    sleep 2
done
echo "进程已退出"

6. 函数封装

函数使脚本模块化,提高复用性和可读性。

6.1 定义与调用

定义格式(两种均可):

function func_name {
    # 命令
}

# 或
func_name() {
    # 命令
}

调用:直接使用函数名,如 func_name

6.2 参数与返回值

  • 参数通过 $1$2 … 获取,$# 参数个数,$@ 所有参数。
  • return 返回数字状态码(0~255),echo 返回字符串。

示例:加法函数

add() {
    local sum=$(( $1 + $2 ))
    echo $sum
}

result=$(add 3 5)
echo "3+5=$result"

带错误处理的函数

check_file() {
    if [[ -f "$1" ]]; then
        return 0
    else
        echo "错误:文件 $1 不存在" >&2
        return 1
    fi
}

if check_file "/etc/hosts"; then
    echo "文件正常"
fi

6.3 变量作用域

默认全局变量,使用 local 声明局部变量(仅在函数内有效)。

my_func() {
    local var="局部"
    echo $var
}
var="全局"
my_func   # 输出 "局部"
echo $var # 输出 "全局"

7. 综合实战:系统健康检查脚本

结合以上所有知识点,编写一个简单的系统检查脚本。

#!/bin/bash
# 系统健康检查

# 函数:检查磁盘使用率
check_disk() {
    local threshold=80
    local usage=$(df -h / | awk 'NR==2 {print $5}' | sed 's/%//')
    if [[ $usage -gt $threshold ]]; then
        echo "警告:根分区使用率已达 ${usage}%"
    else
        echo "根分区使用率正常 (${usage}%)"
    fi
}

# 函数:检查内存
check_memory() {
    local total=$(free -m | awk '/^Mem:/{print $2}')
    local used=$(free -m | awk '/^Mem:/{print $3}')
    local percent=$(( used * 100 / total ))
    echo "内存使用率: ${percent}%"
}

# 函数:检查运行的进程数
check_processes() {
    local count=$(ps aux | wc -l)
    echo "当前进程数: $count"
}

# 主循环:每隔5秒检查一次(按需修改)
main() {
    while true; do
        clear
        echo "========== 系统健康状态 =========="
        check_disk
        check_memory
        check_processes
        echo "=================================="
        echo "按 Ctrl+C 退出"
        sleep 5
    done
}

# 执行主函数
main

8. 最佳实践与注意事项

  1. 引号保护变量:始终用双引号包围变量,防止空格或特殊字符导致错误,如 "$var"
  2. 使用 [[ ]] 代替 [ ](在 Bash 脚本中)以避免复杂的转义和逻辑错误。
  3. 函数应返回状态码:通过 return 返回成功/失败,便于调用者判断。
  4. 避免使用 test-a-o,改用 &&||(在 [[ ]] 中或独立命令)。
  5. 调试技巧:使用 set -x 开启跟踪,set -e 使脚本遇错即退出。
  6. 脚本头部:使用 #!/bin/bash 指明解释器,并添加 set -o pipefail 增强错误处理。

结语

Shell 脚本中的条件测试、分支判断、循环和函数是构建自动化任务的四大基石。通过本文的梳理和示例,希望你对这些结构有了更清晰的认识。在实际工作中,多写多练,结合系统管理需求不断优化,你的脚本功力定会与日俱增。

如果你有任何疑问或想深入了解某个主题,欢迎在评论区留言交流!


Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐