shell-scripting

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Shell Scripting

Shell脚本编写

Purpose

用途

Provides patterns and best practices for writing maintainable shell scripts with error handling, argument parsing, and portability considerations. Covers POSIX sh vs Bash decision-making, parameter expansion, integration with common utilities (jq, yq, awk), and testing with ShellCheck and Bats.
提供编写可维护Shell脚本的模式与最佳实践,涵盖错误处理、参数解析和可移植性考量。包括POSIX sh与Bash的选择决策、参数扩展、与常用工具(jq、yq、awk)的集成,以及使用ShellCheck和Bats进行测试的方法。

When to Use This Skill

适用场景

Use shell scripting when:
  • Orchestrating existing command-line tools and system utilities
  • Writing CI/CD pipeline scripts (GitHub Actions, GitLab CI)
  • Creating container entrypoints and initialization scripts
  • Automating system administration tasks (backups, log rotation)
  • Building development tooling (build scripts, test runners)
Consider Python/Go instead when:
  • Complex business logic or data structures required
  • Cross-platform GUI needed
  • Heavy API integration (REST, gRPC)
  • Script exceeds 200 lines with significant logic complexity
在以下场景中使用Shell脚本:
  • 编排现有命令行工具与系统实用程序
  • 编写CI/CD流水线脚本(GitHub Actions、GitLab CI)
  • 创建容器入口点与初始化脚本
  • 自动化系统管理任务(备份、日志轮转)
  • 构建开发工具链(构建脚本、测试运行器)
在以下场景中考虑使用Python/Go替代:
  • 需要复杂业务逻辑或数据结构
  • 需要跨平台GUI
  • 大量API集成(REST、gRPC)
  • 脚本逻辑复杂度高且代码量超过200行

POSIX sh vs Bash

POSIX sh 与 Bash

Use POSIX sh (#!/bin/sh) when:
  • Maximum portability required (Linux, macOS, BSD, Alpine)
  • Minimal container images needed
  • Embedded systems or unknown target environments
Use Bash (#!/bin/bash) when:
  • Controlled environment (specific OS, container)
  • Arrays or associative arrays needed
  • Advanced parameter expansion beneficial
  • Process substitution
    <(cmd)
    useful
For detailed comparison and testing strategies, see
references/portability-guide.md
.
选择POSIX sh(#!/bin/sh)的场景:
  • 需要最大程度的可移植性(Linux、macOS、BSD、Alpine)
  • 需要最小化容器镜像
  • 嵌入式系统或目标环境未知
选择Bash(#!/bin/bash)的场景:
  • 受控环境(特定操作系统、容器)
  • 需要数组或关联数组
  • 高级参数扩展能带来便利
  • 进程替换
    <(cmd)
    有用
如需详细对比与测试策略,请参阅
references/portability-guide.md

Essential Error Handling

核心错误处理

Fail-Fast Pattern

快速失败模式

bash
#!/bin/bash
set -euo pipefail
bash
#!/bin/bash
set -euo pipefail

-e: Exit on error

-e: 遇到错误时退出

-u: Exit on undefined variable

-u: 遇到未定义变量时退出

-o pipefail: Pipeline fails if any command fails

-o pipefail: 管道中任意命令失败则整个管道失败


Use for production automation, CI/CD scripts, and critical operations.

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

Explicit Exit Code Checking

显式退出码检查

bash
#!/bin/bash

if ! command_that_might_fail; then
    echo "Error: Command failed" >&2
    exit 1
fi
Use for custom error messages and interactive scripts.
bash
#!/bin/bash

if ! command_that_might_fail; then
    echo "Error: Command failed" >&2
    exit 1
fi
适用于自定义错误消息和交互式脚本。

Trap Handlers for Cleanup

清理操作的陷阱处理器

bash
#!/bin/bash
set -euo pipefail

TEMP_FILE=$(mktemp)

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

trap cleanup EXIT
Use for guaranteed cleanup of temporary files, locks, and resources.
For comprehensive error patterns, see
references/error-handling.md
.
bash
#!/bin/bash
set -euo pipefail

TEMP_FILE=$(mktemp)

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

trap cleanup EXIT
用于保证临时文件、锁和资源的清理。
如需完整的错误处理模式,请参阅
references/error-handling.md

Argument Parsing

参数解析

Short Options with getopts (POSIX)

使用getopts实现短选项(POSIX兼容)

bash
#!/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))
bash
#!/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))

Long Options (Manual Parsing)

长选项(手动解析)

bash
#!/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
For hybrid approaches and validation patterns, see
references/argument-parsing.md
.
bash
#!/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

Parameter Expansion Quick Reference

参数扩展速查

bash
undefined
bash
undefined

Default values

默认值

${var:-default} # Use default if unset ${var:=default} # Assign default if unset : "${API_KEY:?Error: required}" # Error if unset
${var:-default} # 变量未设置时使用默认值 ${var:=default} # 变量未设置时赋值默认值 : "${API_KEY:?Error: required}" # 变量未设置时抛出错误

String manipulation

字符串操作

${#var} # String length ${var:offset:length} # Substring ${var%.txt} # Remove suffix ${var##*/} # Basename ${var/old/new} # Replace first ${var//old/new} # Replace all
${#var} # 字符串长度 ${var:offset:length} # 子字符串 ${var%.txt} # 移除后缀 ${var##*/} # 获取文件名 ${var/old/new} # 替换第一个匹配项 ${var//old/new} # 替换所有匹配项

Case conversion (Bash 4+)

大小写转换(Bash 4+)

${var^^} # Uppercase ${var,,} # Lowercase

For complete expansion patterns and array handling, see `references/parameter-expansion.md`.
${var^^} # 转换为大写 ${var,,} # 转换为小写

如需完整的扩展模式和数组处理方法,请参阅`references/parameter-expansion.md`。

Common Utilities Integration

常用工具集成

JSON with jq

使用jq处理JSON

bash
undefined
bash
undefined

Extract field

提取字段

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

Filter array

过滤数组

active=$(jq '.users[] | select(.active) | .name' data.json)
active=$(jq '.users[] | select(.active) | .name' data.json)

Check existence

检查字段是否存在

if ! echo "$json" | jq -e '.field' >/dev/null; then echo "Error: Field missing" >&2 fi
undefined
if ! echo "$json" | jq -e '.field' >/dev/null; then echo "Error: Field missing" >&2 fi
undefined

YAML with yq

使用yq处理YAML

bash
undefined
bash
undefined

Read value (yq v4)

读取值(yq v4)

host=$(yq eval '.database.host' config.yaml)
host=$(yq eval '.database.host' config.yaml)

Update in-place

原地更新

yq eval '.port = 5432' -i config.yaml
yq eval '.port = 5432' -i config.yaml

Convert to JSON

转换为JSON

yq eval -o=json config.yaml
undefined
yq eval -o=json config.yaml
undefined

Text Processing

文本处理

bash
undefined
bash
undefined

awk: Extract columns

awk: 提取列

awk -F',' '{print $1, $3}' data.csv
awk -F',' '{print $1, $3}' data.csv

sed: Replace text

sed: 替换文本

sed 's/old/new/g' file.txt
sed 's/old/new/g' file.txt

grep: Pattern match

grep: 模式匹配

grep -E "ERROR|WARN" logfile.txt

For detailed examples and best practices, see `references/common-utilities.md`.
grep -E "ERROR|WARN" logfile.txt

如需详细示例和最佳实践,请参阅`references/common-utilities.md`。

Testing and Validation

测试与验证

ShellCheck: Static Analysis

ShellCheck:静态分析

bash
undefined
bash
undefined

Check script

检查脚本

shellcheck script.sh
shellcheck script.sh

POSIX compliance

检查POSIX合规性

shellcheck --shell=sh script.sh
shellcheck --shell=sh script.sh

Exclude warnings

排除指定警告

shellcheck --exclude=SC2086 script.sh
undefined
shellcheck --exclude=SC2086 script.sh
undefined

Bats: Automated Testing

Bats:自动化测试

bash
#!/usr/bin/env bats

@test "script runs successfully" {
    run ./script.sh --help
    [ "$status" -eq 0 ]
    [ "${lines[0]}" = "Usage: script.sh [OPTIONS]" ]
}

@test "handles missing argument" {
    run ./script.sh
    [ "$status" -eq 1 ]
    [[ "$output" =~ "Error" ]]
}
Run tests:
bash
bats test/
For CI/CD integration and debugging techniques, see
references/testing-guide.md
.
bash
#!/usr/bin/env bats

@test "script runs successfully" {
    run ./script.sh --help
    [ "$status" -eq 0 ]
    [ "${lines[0]}" = "Usage: script.sh [OPTIONS]" ]
}

@test "handles missing argument" {
    run ./script.sh
    [ "$status" -eq 1 ]
    [[ "$output" =~ "Error" ]]
}
运行测试:
bash
bats test/
如需CI/CD集成和调试技巧,请参阅
references/testing-guide.md

Defensive Programming Checklist

防御式编程检查清单

bash
#!/bin/bash
set -euo pipefail
bash
#!/bin/bash
set -euo pipefail

Check required commands

检查依赖命令是否存在

command -v jq >/dev/null 2>&1 || { echo "Error: jq required" >&2 exit 1 }
command -v jq >/dev/null 2>&1 || { echo "Error: jq required" >&2 exit 1 }

Check environment variables

检查环境变量

: "${API_KEY:?Error: API_KEY required}"
: "${API_KEY:?Error: API_KEY required}"

Check files

检查文件是否存在

[ -f "$CONFIG_FILE" ] || { echo "Error: Config not found: $CONFIG_FILE" >&2 exit 1 }
[ -f "$CONFIG_FILE" ] || { echo "Error: Config not found: $CONFIG_FILE" >&2 exit 1 }

Quote all variables

为所有变量添加引号

echo "Processing: $file" # ❌ Unquoted echo "Processing: "$file"" # ✅ Quoted
undefined
echo "Processing: $file" # ❌ 未加引号 echo "Processing: "$file"" # ✅ 已加引号
undefined

Platform Considerations

平台兼容性考量

macOS vs Linux Differences

macOS与Linux的差异

bash
undefined
bash
undefined

sed in-place

sed原地修改

sed -i '' 's/old/new/g' file.txt # macOS sed -i 's/old/new/g' file.txt # Linux
sed -i '' 's/old/new/g' file.txt # macOS sed -i 's/old/new/g' file.txt # Linux

Portable: Use temp file

可移植方案:使用临时文件

sed 's/old/new/g' file.txt > file.txt.tmp mv file.txt.tmp file.txt
sed 's/old/new/g' file.txt > file.txt.tmp mv file.txt.tmp file.txt

readlink

readlink命令

readlink -f /path # Linux only cd "$(dirname "$0")" && pwd # Portable

For complete platform differences, see `references/portability-guide.md`.
readlink -f /path # 仅Linux支持 cd "$(dirname "$0")" && pwd # 可移植方案

如需完整的平台差异说明,请参阅`references/portability-guide.md`。

Script Categories

脚本分类

System Administration: Cron jobs, log rotation, backup automation Build/Deployment: CI/CD pipelines, Docker builds, deployments Development Tooling: Project setup, test runners, code generators Container Entrypoints: Initialization, signal handling, configuration
系统管理类: 定时任务、日志轮转、备份自动化 构建/部署类: CI/CD流水线、Docker构建、部署脚本 开发工具类: 项目初始化、测试运行器、代码生成器 容器入口点类: 初始化、信号处理、配置脚本

Production Script Template

生产级脚本模板

bash
#!/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() {
    # Check dependencies
    command -v jq >/dev/null 2>&1 || exit 1

    # Parse arguments
    # Validate input
    # Process
    # Report results

    log "Completed successfully"
}

main "$@"
For complete production template, see
examples/production-template.sh
.
bash
#!/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 "Completed successfully"
}

main "$@"
如需完整的生产级模板,请参阅
examples/production-template.sh

Tool Recommendations

工具推荐

Core Tools:
  • jq: JSON parsing and transformation
  • yq: YAML parsing (v4 recommended)
  • ShellCheck: Static analysis and linting
  • Bats: Automated testing framework
Installation:
bash
undefined
核心工具:
  • jq:JSON解析与转换
  • yq:YAML解析(推荐v4版本)
  • ShellCheck:静态分析与代码检查
  • Bats:自动化测试框架
安装方法:
bash
undefined

macOS

macOS

brew install jq yq shellcheck bats-core
brew install jq yq shellcheck bats-core

Ubuntu/Debian

Ubuntu/Debian

apt-get install jq shellcheck
undefined
apt-get install jq shellcheck
undefined

Related Skills

相关技能

  • linux-administration: System commands and administration
  • building-ci-pipelines: Using scripts in CI/CD
  • infrastructure-as-code: Terraform/Pulumi wrappers
  • kubernetes-operations: kubectl scripts, Helm hooks
  • writing-dockerfiles: Container entrypoints
  • linux-administration:系统命令与管理
  • building-ci-pipelines:在CI/CD中使用脚本
  • infrastructure-as-code:Terraform/Pulumi包装器
  • kubernetes-operations:kubectl脚本、Helm钩子
  • writing-dockerfiles:容器入口点编写

Additional Resources

额外资源

Reference Files:
  • references/error-handling.md
    - Comprehensive error patterns
  • references/argument-parsing.md
    - Advanced parsing techniques
  • references/parameter-expansion.md
    - Complete expansion reference
  • references/portability-guide.md
    - POSIX vs Bash differences
  • references/testing-guide.md
    - ShellCheck and Bats guide
  • references/common-utilities.md
    - jq, yq, awk, sed usage
Example Scripts:
  • examples/production-template.sh
    - Production-ready template
  • examples/getopts-basic.sh
    - Simple getopts usage
  • examples/getopts-advanced.sh
    - Complex option handling
  • examples/long-options.sh
    - Manual long option parsing
  • examples/error-handling.sh
    - Error handling patterns
  • examples/json-yaml-processing.sh
    - jq/yq examples
Utility Scripts:
  • scripts/lint-script.sh
    - ShellCheck wrapper for CI
  • scripts/test-script.sh
    - Bats wrapper for CI
参考文档:
  • references/error-handling.md
    - 完整的错误处理模式
  • references/argument-parsing.md
    - 高级参数解析技巧
  • references/parameter-expansion.md
    - 完整的参数扩展参考
  • references/portability-guide.md
    - POSIX与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包装器