🔀 第20课:变量与条件

阶段四:Shell脚本第2/6课

📚 课程目标

一、变量进阶

1.1 字符串操作

str="Hello, World!"
echo ${#str}              # 13 (长度)
echo ${str:7}             # World! (从第7位截取)
echo ${str:0:5}           # Hello (截取5个字符)
echo ${str/World/Linux}   # Hello, Linux! (替换第一个)
echo ${str//l/L}          # HeLLo, WorLd! (替换所有)

# 默认值
echo ${UNSET:-default}    # default (变量未设置则用默认值)
echo ${EMPTY:=default}    # default (变量为空则赋值)
echo ${MISSING:+set}      # 空 (变量设置了则用替代值)

1.2 数组

# 普通数组
fruits=("apple" "banana" "cherry")
echo ${fruits[0]}         # apple
echo ${fruits[@]}         # 所有元素
echo ${#fruits[@]}        # 3 (元素个数)
echo ${!fruits[@]}        # 0 1 2 (索引)

# 添加元素
fruits+=("date")

# 遍历
for f in "${fruits[@]}"; do
    echo "水果: $f"
done

# 关联数组(Bash 4+)
declare -A config
config[host]="server1"
config[port]="8080"
echo ${config[host]}      # server1

二、条件判断

2.1 文件测试

操作符含义
-f普通文件
-d目录
-e存在
-r/-w/-x可读/写/执行
-s大小>0
-L符号链接

2.2 字符串测试

操作符含义
-z为空
-n非空
==相等
!=不等
=~正则匹配(仅[[ ]])

2.3 数值比较

操作符含义
-eq等于
-ne不等于
-gt/-ge大于/大于等于
-lt/-le小于/小于等于

三、if分支

# 基本if
if [[ -f /etc/passwd ]]; then
    echo "文件存在"
fi

# if-else
if [[ $EUID -eq 0 ]]; then
    echo "root用户"
else
    echo "普通用户"
fi

# if-elif-else
if [[ $score -ge 90 ]]; then
    echo "优秀"
elif [[ $score -ge 80 ]]; then
    echo "良好"
elif [[ $score -ge 60 ]]; then
    echo "及格"
else
    echo "不及格"
fi

# 逻辑组合
if [[ -f "$file" ]] && [[ -r "$file" ]]; then
    echo "文件存在且可读"
fi

四、case分支

case "$1" in
    start)   systemctl start "$SERVICE" ;;
    stop)    systemctl stop "$SERVICE" ;;
    restart) systemctl restart "$SERVICE" ;;
    status)  systemctl status "$SERVICE" ;;
    *)       echo "用法: $0 {start|stop|restart|status}"; exit 1 ;;
esac

# 模式匹配
case "$ext" in
    jpg|jpeg|png|gif) echo "图片" ;;
    mp4|avi|mkv)      echo "视频" ;;
    *)                echo "未知: $ext" ;;
esac

五、退出码

# 退出码约定
# 0: 成功  1: 一般错误  127: 命令未找到
# 128+N: 信号N导致退出

if [[ -z "$1" ]]; then
    echo "需要参数" >&2
    exit 1
fi

# 短路语法
mkdir -p /tmp/test && echo "成功" || echo "失败"

六、实战:配置文件解析

#!/bin/bash
# 解析 KEY=VALUE 格式配置文件
CONFIG="$1"

while IFS='=' read -r key value; do
    [[ "$key" =~ ^[[:space:]]*# ]] && continue
    [[ -z "$key" ]] && continue
    key=$(echo "$key" | xargs)
    value=$(echo "$value" | xargs)
    declare -g "$key=$value"
    echo "$key = $value"
done < "$CONFIG"

七、练习

📝 条件判断脚本

#!/bin/bash
# check-file.sh
target="$1"
[[ -z "$target" ]] && echo "用法: $0 <路径>" && exit 1

if [[ ! -e "$target" ]]; then
    echo "不存在: $target"; exit 1
elif [[ -f "$target" ]]; then
    echo "文件: $target"
elif [[ -d "$target" ]]; then
    echo "目录: $target"
fi

[[ -r "$target" ]] && echo "可读"
[[ -w "$target" ]] && echo "可写"
[[ -x "$target" ]] && echo "可执行"
echo "大小: $(stat -c '%s' "$target") bytes"
🏆

成就解锁:条件判断师

✅ 掌握变量与字符串操作

✅ 理解数组

✅ 掌握条件操作符

✅ 学会if/case分支

✅ 理解退出码

八、高级条件模式

# 多条件组合
if [[ -f "$file" && -r "$file" && -s "$file" ]]; then
    echo "文件存在、可读、非空"
fi

# 嵌套条件
if [[ $EUID -eq 0 ]]; then
    if [[ -f /etc/shadow ]]; then
        echo "root且shadow文件存在"
    fi
fi

# 条件赋值
[[ -z "$PORT" ]] && PORT=8080
[[ -z "$HOST" ]] && HOST="localhost"

# 一次性判断多个
if [[ $status == "running" || $status == "active" ]]; then
    echo "服务正常"
fi

九、Bash扩展模式匹配

# 需要shopt -s extglob
shopt -s extglob

# 删除所有.log和.tmp文件
rm -+(*.log|*.tmp)

# 匹配不是.conf的文件
ls !(*.conf)

# 匹配一个或多个数字
echo "${path/+([0-9])/NUM}"

# 匹配除v1 v2外的版本
case "$ver" in
    v[!12]*) echo "新版本" ;;
esac

十、getopts参数解析

#!/bin/bash
while getopts "hvd:f:" opt; do
    case $opt in
        h) echo "Usage: $0 [-h] [-v] [-d dir] [-f file]"; exit 0 ;;
        v) VERBOSE=1 ;;
        d) DIR="$OPTARG" ;;
        f) FILE="$OPTARG" ;;
        :) echo "选项 -$OPTARG 需要参数" >&2; exit 1 ;;
        \?) echo "无效选项: -$OPTARG" >&2; exit 1 ;;
    esac
done
shift $((OPTIND - 1))

echo "DIR=$DIR FILE=$FILE VERBOSE=$VERBOSE"
echo "剩余参数: $@"

附录:变量与条件判断学习资源

推荐书籍

书名作者重点
Linux命令行与Shell脚本编程大全BlumShell脚本全面覆盖
UNIX/Linux系统管理技术手册Nemeth系统管理圣经
Ansible: Up and RunningLintnerAnsible实战
鸟哥的Linux私房菜鸟哥中文经典入门

在线资源

认证路径

认证机构级别
LFCSLinux Foundation基础系统管理
LFCELinux Foundation高级系统工程师
RHCSARed HatRed Hat管理员
RHCERed HatRed Hat工程师
CKA/CKADCNCFKubernetes管理员/开发者

实践环境

常用速查表

# 文本处理速查
grep -E 'pattern' file     # 扩展正则搜索
sed -i 's/old/new/g' file  # 替换
awk '{print $1}' file     # 提取列
sort | uniq -c | sort -rn   # 频次统计
cut -d: -f1 file            # 切列
tr 'A-Z' 'a-z'             # 转小写

# 系统管理速查
systemctl status/start/stop/restart service
journalctl -u service -f    # 跟踪日志
df -h / du -sh *            # 磁盘使用
ps aux --sort=-%mem         # 内存排序
ss -tlnp                    # 监听端口
iptables -L -n              # 防火墙规则
crontab -e                  # 编辑定时任务
useradd -m -s /bin/bash user # 创建用户

# 网络诊断速查
ip addr / ip route          # 网络配置
ping / traceroute           # 连通测试
dig / nslookup              # DNS查询
curl -I url                 # HTTP头检查
nc -zv host port            # 端口测试
tcpdump -i eth0 port 80     # 抓包

十、实机验证记录

$ [[ -f /etc/passwd ]] && echo "存在" || echo "不存在"
存在
$ [[ -d /tmp ]] && echo "是目录" || echo "不是"
是目录
$ test 5 -gt 3 && echo "5>3" || echo "5<=3"
5>3
$ id
uid=0(root) gid=0(root) groups=0(root)

十二、生产环境条件判断模式

#!/bin/bash
# safe-restart.sh - 安全重启服务
SERVICE="$1"
[[ -z "$SERVICE" ]] && echo "用法: $0 service_name" >&2 && exit 1

# 检查服务是否存在
if ! systemctl list-unit-files "$SERVICE.service" &>/dev/null; then
    echo "服务不存在: $SERVICE" >&2; exit 1
fi

# 检查当前状态
if systemctl is-active "$SERVICE" &>/dev/null; then
    echo "服务正在运行,准备重启..."
    systemctl restart "$SERVICE"
    sleep 2
    if systemctl is-active "$SERVICE"; then
        echo "✅ 重启成功"
    else
        echo "❌ 重启后服务未运行!" >&2; exit 1
    fi
else
    echo "服务未运行,尝试启动..."
    systemctl start "$SERVICE"
    if systemctl is-active "$SERVICE"; then
        echo "✅ 启动成功"
    else
        echo "❌ 启动失败!" >&2; exit 1
    fi
fi

十二、生产环境条件模式

#!/bin/bash
# safe-restart.sh
SERVICE="$1"
[[ -z "$SERVICE" ]] && echo "Usage: $0 service" >&2 && exit 1
if ! systemctl list-unit-files "$SERVICE.service" &>/dev/null; then
    echo "服务不存在: $SERVICE" >&2; exit 1
fi
if systemctl is-active "$SERVICE" &>/dev/null; then
    echo "重启 $SERVICE..."
    systemctl restart "$SERVICE"
    sleep 2
    systemctl is-active "$SERVICE" && echo "OK" || echo "FAIL" >&2
else
    systemctl start "$SERVICE"
    systemctl is-active "$SERVICE" && echo "OK" || echo "FAIL" >&2
fi

专题:变量作用域与条件表达式

在实际生产环境中,变量作用域与条件表达式是系统管理员必须深入掌握的核心能力。本节将探讨常见场景、最佳实践和避坑指南。

常见问题与解决方案

问题1:脚本在生产环境运行异常

开发环境和生产环境的差异是脚本出错的常见原因。建议:

问题2:性能不达预期

优化策略:

问题3:可维护性差

改善方法:

最佳实践总结

#!/bin/bash
# 变量作用域与条件表达式最佳实践模板
set -euo pipefail

# 1. 参数校验
[[ $# -lt 1 ]] && echo "Usage: $0 <arg>" >&2 && exit 1

# 2. 环境检查
require_cmd() {
    command -v "$1" >/dev/null || { echo "需要: $1" >&2; exit 1; }
}

# 3. 清理机制
TMPDIR=$(mktemp -d)
trap 'rm -rf "$TMPDIR"' EXIT

# 4. 日志记录
log() { echo "[$(date +%T)] $*"; }
err() { log "ERROR: $*" >&2; exit 1; }

# 5. 主逻辑
main() {
    log "开始处理: $1"
    # 业务逻辑
    log "处理完成"
}

main "$@"

进阶方向