Shell脚本编写Skill shell-scripting

Shell 脚本编写技能用于自动化系统任务、构建 CI/CD 管道脚本、创建容器入口点等。它涉及错误处理、参数解析、测试和可移植性优化,适用于 DevOps、系统管理和开发自动化。关键词:Shell 脚本、自动化、DevOps、CI/CD、容器、错误处理、参数解析、测试、可移植性。

DevOps 0 次安装 0 次浏览 更新于 3/23/2026

name: shell-scripting description: 编写健壮、可移植的 Shell 脚本,具有正确的错误处理、参数解析和测试。适用于自动化系统任务、构建 CI/CD 脚本或创建容器入口点。

Shell 脚本编写

目的

提供编写可维护 Shell 脚本的模式和最佳实践,包括错误处理、参数解析和可移植性考虑。涵盖 POSIX sh 与 Bash 的决策、参数扩展、与常见工具(jq, yq, awk)的集成,以及使用 ShellCheck 和 Bats 进行测试。

何时使用此技能

在以下情况下使用 Shell 脚本编写:

  • 编排现有命令行工具和系统实用程序
  • 编写 CI/CD 管道脚本(GitHub Actions, GitLab CI)
  • 创建容器入口点和初始化脚本
  • 自动化系统管理任务(备份、日志轮转)
  • 构建开发工具(构建脚本、测试运行器)

考虑使用 Python/Go 而不是 Shell 脚本,当:

  • 需要复杂的业务逻辑或数据结构
  • 需要跨平台 GUI
  • 大量 API 集成(REST, gRPC)
  • 脚本超过 200 行且有显著的逻辑复杂性

POSIX sh vs Bash

使用 POSIX sh (#!/bin/sh) 当:

  • 需要最大可移植性(Linux, macOS, BSD, Alpine)
  • 需要最小化容器镜像
  • 嵌入式系统或未知目标环境

使用 Bash (#!/bin/bash) 当:

  • 控制环境(特定操作系统、容器)
  • 需要数组或关联数组
  • 高级参数扩展有益
  • 进程替换 <(cmd) 有用

详细比较和测试策略,参见 references/portability-guide.md

基本错误处理

快速失败模式

#!/bin/bash
set -euo pipefail

# -e: 出错时退出
# -u: 未定义变量时退出
# -o pipefail: 管道中任何命令失败则失败

用于生产自动化、CI/CD 脚本和关键操作。

显式退出代码检查

#!/bin/bash

if ! command_that_might_fail; then
    echo "错误:命令失败" >&2
    exit 1
fi

用于自定义错误消息和交互式脚本。

用于清理的陷阱处理器

#!/bin/bash
set -euo pipefail

TEMP_FILE=$(mktemp)

cleanup() {
    rm -f "$TEMP_FILE"
}

trap cleanup EXIT

用于确保清理临时文件、锁和资源。

综合错误模式,参见 references/error-handling.md

参数解析

使用 getopts 的短选项(POSIX)

#!/bin/bash

while getopts "hvf:o:" opt; do
    case "$opt" in
        h) usage ;;
        v) VERBOSE=true ;;
        f) INPUT_FILE="$OPTARG" ;;
        o) OUTPUT_FILE="$OPTARG" ;;
        *) usage ;;
    esac
done

shift $((OPTIND - 1))

长选项(手动解析)

#!/bin/bash

while [[ $# -gt 0 ]]; do
    case "$1" in
        --help) usage ;;
        --verbose) VERBOSE=true; shift ;;
        --file) INPUT_FILE="$2"; shift 2 ;;
        --file=*) INPUT_FILE="${1#*=}"; shift ;;
        *) break ;;
    esac
done

混合方法和验证模式,参见 references/argument-parsing.md

参数扩展快速参考

# 默认值
${var:-default}              # 如果未设置则使用默认值
${var:=default}              # 如果未设置则分配默认值
: "${API_KEY:?错误:必需}"  # 如果未设置则报错

# 字符串操作
${#var}                      # 字符串长度
${var:offset:length}         # 子字符串
${var%.txt}                  # 移除后缀
${var##*/}                   # 基本名称
${var/old/new}               # 替换第一个
${var//old/new}              # 替换全部

# 大小写转换(Bash 4+)
${var^^}                     # 大写
${var,,}                     # 小写

完整扩展模式和数组处理,参见 references/parameter-expansion.md

常见实用程序集成

使用 jq 处理 JSON

# 提取字段
name=$(curl -sSL https://api.example.com/user | jq -r '.name')

# 过滤数组
active=$(jq '.users[] | select(.active) | .name' data.json)

# 检查存在性
if ! echo "$json" | jq -e '.field' >/dev/null; then
    echo "错误:字段缺失" >&2
fi

使用 yq 处理 YAML

# 读取值(yq v4)
host=$(yq eval '.database.host' config.yaml)

# 原地更新
yq eval '.port = 5432' -i config.yaml

# 转换为 JSON
yq eval -o=json config.yaml

文本处理

# awk: 提取列
awk -F',' '{print $1, $3}' data.csv

# sed: 替换文本
sed 's/old/new/g' file.txt

# grep: 模式匹配
grep -E "ERROR|WARN" logfile.txt

详细示例和最佳实践,参见 references/common-utilities.md

测试和验证

ShellCheck: 静态分析

# 检查脚本
shellcheck script.sh

# POSIX 合规性
shellcheck --shell=sh script.sh

# 排除警告
shellcheck --exclude=SC2086 script.sh

Bats: 自动化测试

#!/usr/bin/env bats

@test "脚本运行成功" {
    run ./script.sh --help
    [ "$status" -eq 0 ]
    [ "${lines[0]}" = "用法:script.sh [选项]" ]
}

@test "处理缺少参数" {
    run ./script.sh
    [ "$status" -eq 1 ]
    [[ "$output" =~ "错误" ]]
}

运行测试:

bats test/

CI/CD 集成和调试技术,参见 references/testing-guide.md

防御性编程检查清单

#!/bin/bash
set -euo pipefail

# 检查必需命令
command -v jq >/dev/null 2>&1 || {
    echo "错误:需要 jq" >&2
    exit 1
}

# 检查环境变量
: "${API_KEY:?错误:需要 API_KEY}"

# 检查文件
[ -f "$CONFIG_FILE" ] || {
    echo "错误:配置文件未找到:$CONFIG_FILE" >&2
    exit 1
}

# 引用所有变量
echo "处理:$file"        # ❌ 未引用
echo "处理:\"$file\""    # ✅ 引用

平台考虑

macOS vs Linux 差异

# sed 原地操作
sed -i '' 's/old/new/g' file.txt    # macOS
sed -i 's/old/new/g' file.txt       # Linux

# 可移植性:使用临时文件
sed 's/old/new/g' file.txt > file.txt.tmp
mv file.txt.tmp file.txt

# readlink
readlink -f /path                    # 仅 Linux
cd "$(dirname "$0")" && pwd         # 可移植

完整平台差异,参见 references/portability-guide.md

脚本类别

系统管理: Cron 任务、日志轮转、备份自动化 构建/部署: CI/CD 管道、Docker 构建、部署 开发工具: 项目设置、测试运行器、代码生成器 容器入口点: 初始化、信号处理、配置

生产脚本模板

#!/bin/bash
set -euo pipefail

readonly SCRIPT_NAME="$(basename "$0")"
readonly SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"

TEMP_DIR=""

cleanup() {
    local exit_code=$?
    rm -rf "$TEMP_DIR"
    exit "$exit_code"
}

trap cleanup EXIT

log() {
    echo "[$(date +'%Y-%m-%d %H:%M:%S')] $*" >&2
}

main() {
    # 检查依赖项
    command -v jq >/dev/null 2>&1 || exit 1

    # 解析参数
    # 验证输入
    # 处理
    # 报告结果

    log "成功完成"
}

main "$@"

完整生产模板,参见 examples/production-template.sh

工具推荐

核心工具:

  • jq: JSON 解析和转换
  • yq: YAML 解析(推荐 v4)
  • ShellCheck: 静态分析和 linting
  • Bats: 自动化测试框架

安装:

# macOS
brew install jq yq shellcheck bats-core

# Ubuntu/Debian
apt-get install jq shellcheck

相关技能

  • linux-administration: 系统命令和管理
  • building-ci-pipelines: 在 CI/CD 中使用脚本
  • infrastructure-as-code: Terraform/Pulumi 包装器
  • kubernetes-operations: kubectl 脚本、Helm 钩子
  • writing-dockerfiles: 容器入口点

附加资源

参考文件:

  • references/error-handling.md - 综合错误模式
  • references/argument-parsing.md - 高级解析技术
  • references/parameter-expansion.md - 完整扩展参考
  • references/portability-guide.md - POSIX vs Bash 差异
  • references/testing-guide.md - ShellCheck 和 Bats 指南
  • references/common-utilities.md - jq, yq, awk, sed 使用

示例脚本:

  • examples/production-template.sh - 生产就绪模板
  • examples/getopts-basic.sh - 简单 getopts 使用
  • examples/getopts-advanced.sh - 复杂选项处理
  • examples/long-options.sh - 手动长选项解析
  • examples/error-handling.sh - 错误处理模式
  • examples/json-yaml-processing.sh - jq/yq 示例

实用脚本:

  • scripts/lint-script.sh - 用于 CI 的 ShellCheck 包装器
  • scripts/test-script.sh - 用于 CI 的 Bats 包装器