Lesson 0004:Shell 脚本——把命令变成程序

你已经在终端里敲过几十条命令了。但 DevOps 不是一条一条敲的——是写成脚本,自动跑的

CI/CD 流水线的每一个步骤、服务器的启动脚本、定时备份任务、健康检查——本质都是 Shell 脚本。这节课让你从「手动敲命令」升级到「写脚本自动执行」。

Shell 脚本是什么?

一个 Shell 脚本就是一个包含一系列命令的文本文件,你给它执行权限,它按顺序跑。

#!/bin/bash
# 这是我的第一个脚本
echo "Hello, DevOps!"
date
whoami

拆解:

#!/bin/bash vs #!/bin/sh:bash 是 sh 的超集,有更多功能(数组、[[ ]]、更好的语法)。Ubuntu 上 /bin/sh 是 dash(更小更快但功能少)。DevOps 脚本默认用 bash。

第一部分:核心语法(15 分钟)

在终端里跟着敲。边敲边理解。

1. 变量

#!/bin/bash
APP_NAME="my-app"          # 定义变量(等号两边不能有空格!)
VERSION=1.0.0
echo "Deploying $APP_NAME v$VERSION"   # $变量名 取值
echo "Path: ${APP_NAME}-${VERSION}"    # ${} 避免歧义

# 命令替换:把命令的输出赋给变量
CURRENT_DATE=$(date +%Y-%m-%d)
echo "Today is $CURRENT_DATE"

2. 条件判断

#!/bin/bash
if [ "$1" == "production" ]; then     # $1 = 第一个命令行参数
    echo "⚠️  Deploying to PRODUCTION!"
elif [ "$1" == "staging" ]; then
    echo "Deploying to staging..."
else
    echo "Usage: $0 "
    exit 1                            # 非 0 = 出错
fi

# 文件/目录判断(DevOps 高频)
if [ -f ".env" ]; then                # -f = 文件存在?
    echo ".env file found"
fi
if [ -d "/var/log" ]; then            # -d = 目录存在?
    echo "log directory exists"
fi
测试含义
[ -f file ]file 存在且是普通文件
[ -d dir ]dir 存在且是目录
[ -z "$var" ]$var 为空字符串
[ -n "$var" ]$var 非空
[ "$a" == "$b" ]字符串相等
[ $a -eq $b ]整数相等

3. 循环

#!/bin/bash
# 对列表中的每一项执行操作
SERVICES="nginx docker postgresql"
for svc in $SERVICES; do
    echo "Restarting $svc..."
    sudo systemctl restart $svc
done

# 或者遍历命令输出
for container in $(docker ps -q); do
    echo "Container: $container"
done

4. 函数

#!/bin/bash
# 定义函数
check_service() {
    local service_name=$1              # local = 局部变量
    if systemctl is-active --quiet "$service_name"; then
        echo "✅ $service_name is running"
        return 0
    else
        echo "❌ $service_name is DOWN!"
        return 1
    fi
}

# 调用函数
check_service "nginx"
check_service "docker"

第二部分:错误处理——让脚本「靠谱」

默认情况下,Shell 脚本遇到错误不会停——它继续执行下一行,特别危险。

#!/bin/bash
set -e              # 任何命令返回非 0 就立刻退出
set -u              # 使用未定义变量就报错退出
set -o pipefail     # 管道中任何一个命令失败,整个管道算失败

# 这三个通常是 DevOps 脚本的前三行(写在 shebang 之后)

# 如果你预期某个命令可能失败,用 || true
grep "maybe-not-there" /etc/hosts || true   # 找不到也不退出

# 自定义错误处理
trap 'echo "❌ Script failed on line $LINENO"' ERR
DevOps 标准脚本头:每个生产环境脚本的前 5 行应该长这样:
#!/bin/bash
set -euo pipefail
# Script: deploy.sh
# Purpose: Deploy application to target environment

第三部分:实战——写一个部署前检查脚本

这是一个真实 DevOps 场景:部署之前,检查所有前置条件。

#!/bin/bash
set -euo pipefail

# === Config ===
REQUIRED_SPACE_MB=500
REQUIRED_SERVICES="docker"

# === Colors (optional, makes output pretty) ===
RED='\033[0;31m'
GREEN='\033[0;32m'
NC='\033[0m' # No Color

echo "=== Pre-deployment Check ==="

# 1. Check disk space
echo -n "Checking disk space... "
AVAILABLE=$(df / --output=avail | tail -1 | tr -d ' ')
AVAILABLE_MB=$((AVAILABLE / 1024))
if [ "$AVAILABLE_MB" -lt "$REQUIRED_SPACE_MB" ]; then
    echo -e "${RED}FAIL${NC} (${AVAILABLE_MB}MB < ${REQUIRED_SPACE_MB}MB)"
    exit 1
fi
echo -e "${GREEN}OK${NC} (${AVAILABLE_MB}MB)"

# 2. Check required services
for svc in $REQUIRED_SERVICES; do
    echo -n "Checking $svc... "
    if systemctl is-active --quiet "$svc" 2>/dev/null; then
        echo -e "${GREEN}running${NC}"
    else
        echo -e "${RED}not running${NC}"
        exit 1
    fi
done

# 3. Check .env file
echo -n "Checking .env... "
if [ -f ".env" ]; then
    echo -e "${GREEN}found${NC}"
else
    echo -e "${RED}missing${NC}"
    exit 1
fi

echo -e "\n${GREEN}✅ All checks passed! Ready to deploy.${NC}"

把这个保存为 pre-deploy-check.sh,加执行权限,你就有了一套标准化部署检查。

动手练习:写一个服务健康检查脚本

任务:写一个脚本 health-check.sh,接受一个服务名作为参数,检查:

  1. 该服务是否在运行(systemctl is-active
  2. 该服务对应的端口是否在监听(ss -tlnp | grep 端口号)——用 if 判断
  3. 输出带颜色的结果
# 模板(填空)
#!/bin/bash
set -euo pipefail

SERVICE=$1

if [ -z "____" ]; then
    echo "Usage: $0 "
    exit 1
fi

# Check service status
if ______ is-active --quiet "$SERVICE"; then
    echo "✅ $SERVICE is running"
else
    echo "❌ $SERVICE is not running"
    exit 1
fi

# Check port (hint: nginx=80, docker=2375)
# Bonus: make port configurable

小测验

Q1:Shebang #!/bin/bash 的作用是什么?
Q2:set -e 的作用是什么?
Q3:如何把 date 命令的输出赋给变量 TODAY
Q4:脚本的第一个命令行参数用什么变量获取?

推荐资源

下一步

你已经有了命令行基础、Git 进阶、Shell 脚本三项核心技能。下一课我们进入 DevOps 的第一个「重型武器」:Docker 入门——用容器化你的前端/后端项目,告别「在我机器上能跑」。

写脚本卡住了?把脚本贴给我,我帮你 debug。建议装 shellcheck,它比你自己检查快 10 倍。
← Lesson 0003 · 术语表 · Mission