1. Shell脚本三部曲:从入门到精通的系统化学习路径
在Linux系统管理和自动化运维领域,Shell脚本编写能力堪称工程师的"瑞士军刀"。最近在Ubuntu 24.04 LTS发布后,我注意到很多技术社区关于Shell脚本的讨论明显升温——无论是系统初始化配置、批量文件处理,还是自动化测试部署,一个精心设计的Shell脚本往往能节省数小时重复劳动。
这个三部曲系列将系统性地拆解Shell脚本开发的完整知识体系。上篇聚焦基础语法和核心概念,中篇深入流程控制和函数封装,下篇则探讨实战中的高级技巧与性能优化。不同于市面上零散的教程,我们将采用"概念解释→典型场景→避坑指南"的三段式讲解,每个知识点都配有Ubuntu 24.04环境验证过的实例。
提示:本系列所有示例均在Ubuntu 24.04 LTS(Linux 5.15.0内核)测试通过,建议读者使用相同环境进行练习。对于Windows用户,可通过WSL2获得接近原生的体验。
2. 上篇:Shell脚本基础语法精要
2.1 变量定义与字符串操作
Shell变量的定义看似简单,但实际使用中隐藏着许多新手容易踩的坑。以下是经过验证的最佳实践:
bash复制#!/bin/bash
# 声明普通变量(等号两侧不能有空格)
package_name="nginx"
version="1.23.4"
# 只读变量(类似常量)
readonly MAX_RETRY=3
# 使用变量(推荐${}形式)
echo "Installing ${package_name}-${version}..."
# 字符串拼接
download_url="https://example.com/${package_name}/${package_name}-${version}.tar.gz"
字符串操作是Shell脚本中最频繁使用的功能之一。在Ubuntu 24.04的Bash 5.2版本中,这些操作特别值得掌握:
bash复制# 获取字符串长度
path="/usr/local/bin"
echo "Path length: ${#path}" # 输出13
# 子字符串提取(位置从0开始)
echo ${path:5:6} # 输出local/
# 默认值设置(当变量未定义时)
echo "Cache dir: ${CACHE_DIR:-/tmp/cache}"
# 实际案例:处理文件路径
filename="app-2.3.1.deb"
name=${filename%.*} # 移除最后一个.及其后内容 → app-2.3.1
extension=${filename##*.} # 提取最后一个.后内容 → deb
注意:在Ubuntu 24.04中,Bash的字符串索引从0开始,这与某些旧版本不同。如果脚本需要跨版本兼容,务必进行充分测试。
2.2 数组与关联数组实战
现代Bash支持两种数组类型,在配置管理和批量操作中极为实用:
bash复制# 索引数组(适用于有序列表)
services=("nginx" "mysql" "redis")
services+=( "postgresql" ) # 数组追加
echo "First service: ${services[0]}" # 输出nginx
# 遍历数组(避免使用"for i in ${array[@]}"的旧式写法)
for service in "${services[@]}"; do
systemctl is-active "$service" >/dev/null 2>&1 && echo "$service is running"
done
# 关联数组(类似字典,需显式声明)
declare -A config
config["host"]="db.example.com"
config["port"]="5432"
echo "DB host: ${config[host]}" # 输出db.example.com
在Ubuntu 24.04中处理软件包列表时,这种数据结构特别有用:
bash复制# 实际案例:检查多个软件包安装状态
declare -A pkg_status
for pkg in "${packages[@]}"; do
if dpkg -l | grep -q "^ii $pkg"; then
pkg_status["$pkg"]="installed"
else
pkg_status["$pkg"]="missing"
fi
done
2.3 参数传递与用户输入处理
脚本的交互能力直接影响其易用性。以下是经过实战检验的参数处理模式:
bash复制#!/bin/bash
# 基础参数获取($0为脚本名,$1-$9为位置参数)
echo "Executing $0 with $# arguments"
# 现代参数解析(推荐getopts替代传统的getopt)
while getopts ":u:p:d:" opt; do
case $opt in
u) username="$OPTARG" ;;
p) password="$OPTARG" ;;
d) database="${OPTARG:-defaultdb}" ;; # 带默认值
\?) echo "Invalid option -$OPTARG" >&2 ;;
esac
done
# 交互式输入(带超时和默认值)
read -t 10 -p "Enter port [3306]: " port
port=${port:-3306}
# 安全提示输入(不显示内容)
read -s -p "DB password: " db_pass
echo # 换行
在自动化部署场景中,这种参数处理方式可以灵活适应各种需求:
bash复制# 实际案例:MySQL数据库初始化脚本
if [[ -z "$username" || -z "$password" ]]; then
echo "Usage: $0 -u username -p password [-d database]" >&2
exit 1
fi
mysql -u root <<EOF
CREATE DATABASE IF NOT EXISTS ${database};
CREATE USER '${username}'@'%' IDENTIFIED BY '${password}';
GRANT ALL PRIVILEGES ON ${database}.* TO '${username}'@'%';
FLUSH PRIVILEGES;
EOF
3. 中篇:流程控制与函数封装艺术
3.1 条件判断的深层逻辑
Shell中的条件测试语法看似简单,但实际应用中存在许多微妙的差异:
bash复制# 数值比较(使用双括号)
if (( $# < 2 )); then
echo "至少需要2个参数" >&2
exit 1
fi
# 文件测试(生产环境推荐这种写法)
config_file="/etc/app.conf"
[[ -f "$config_file" ]] || {
echo "配置文件 $config_file 不存在" >&2
exit 1
}
# 字符串匹配(支持正则)
domain="mail.example.com"
if [[ "$domain" =~ ^[a-z]+\.example\.com$ ]]; then
echo "Valid subdomain"
fi
在Ubuntu 24.04的系统管理脚本中,这种条件判断模式特别实用:
bash复制# 实际案例:服务状态检查
service_check() {
local svc="$1"
if systemctl is-active --quiet "$svc"; then
echo "[OK] $svc is running"
return 0
else
echo "[FAIL] $svc is not running" >&2
return 1
fi
}
3.2 循环结构的性能优化
不同的循环结构在性能和适用场景上存在显著差异:
bash复制# 传统for循环(适用于已知列表)
for i in {1..5}; do
echo "Processing item $i"
done
# C风格for循环(适合数值操作)
for ((i=0; i<10; i+=2)); do
echo "Even number: $i"
done
# while循环最佳实践(处理动态内容)
while IFS= read -r line; do
[[ "$line" =~ ^# ]] && continue # 跳过注释行
echo "Processing: $line"
done < config.list
在处理大型日志文件时,这种优化技巧可以显著提升性能:
bash复制# 实际案例:高效解析nginx日志
parse_nginx_log() {
local logfile="$1"
declare -A ip_count
while read -r line; do
[[ "$line" =~ ^([0-9.]+) ]] && ((ip_count["${BASH_REMATCH[1]}"]++))
done < "$logfile"
for ip in "${!ip_count[@]}"; do
printf "%-15s → %4d次访问\n" "$ip" "${ip_count[$ip]}"
done | sort -nrk3 | head -10
}
3.3 函数设计与模块化编程
良好的函数设计可以大幅提升脚本的可维护性:
bash复制# 函数定义规范(包含参数检查)
install_package() {
(( $# < 1 )) && { echo "Usage: install_package <name> [version]"; return 1; }
local pkg="$1"
local ver="${2:-latest}"
local log="/var/log/install_${pkg}.log"
echo "[$(date)] Installing $pkg-$ver" | tee "$log"
if apt-get install -y "$pkg=$ver" >> "$log" 2>&1; then
echo "安装成功"
return 0
else
echo "安装失败,查看日志: $log" >&2
return 1
fi
}
# 函数返回值处理
if ! install_package "nginx"; then
exit 1
fi
在复杂脚本中,这种模块化设计模式特别有价值:
bash复制# 实际案例:系统初始化框架
source ./network.sh
source ./security.sh
source ./monitoring.sh
main() {
local start_time=$(date +%s)
init_network || fail "网络配置失败"
setup_firewall || fail "防火墙配置失败"
deploy_monitoring || fail "监控部署失败"
local duration=$(( $(date +%s) - start_time ))
echo "初始化完成,耗时${duration}秒"
}
fail() {
echo "[ERROR] $1" >&2
exit 1
}
main "$@"
4. 下篇:高级技巧与性能调优
4.1 信号处理与进程控制
健壮的脚本需要妥善处理各种中断信号:
bash复制#!/bin/bash
# 信号捕获(支持多个信号)
trap 'cleanup; exit 1' INT TERM HUP
temp_files=()
cleanup() {
echo "正在清理临时文件..."
rm -f "${temp_files[@]}"
}
# 创建临时文件(自动注册清理)
create_tempfile() {
local tf=$(mktemp)
temp_files+=("$tf")
echo "$tf"
}
# 后台进程管理
start_worker() {
while true; do
process_task
sleep 1
done
}
# 启动多个worker
for i in {1..4}; do
start_worker &
pids[$i]=$!
done
# 等待所有worker完成
wait "${pids[@]}"
在长时间运行的运维脚本中,这种模式可以确保资源正确释放:
bash复制# 实际案例:批量服务器配置
configure_servers() {
local servers=("web01" "web02" "db01")
local pids=()
for server in "${servers[@]}"; do
(
ssh "$server" "sudo apt update && sudo apt upgrade -y"
scp configs/ "$server:/tmp/"
ssh "$server" "sudo /tmp/configs/apply.sh"
) &> "log/$server.log" &
pids+=($!)
done
# 设置超时等待
wait_timeout=300
for pid in "${pids[@]}"; do
wait "$pid" || echo "服务器配置超时" >&2
done
}
4.2 性能分析与优化策略
当脚本处理大规模数据时,性能问题会变得突出:
bash复制# 时间测量辅助函数
timeit() {
local start=$(date +%s.%N)
"$@"
local end=$(date +%s.%N)
printf "执行时间: %.2f秒\n" $(echo "$end - $start" | bc)
}
# 避免子shell的开销(使用进程替换替代管道)
slow_version() {
cat bigfile.txt | grep "error" | wc -l
}
fast_version() {
grep "error" bigfile.txt | wc -l
}
faster_version() {
wc -l < <(grep "error" bigfile.txt)
}
echo "慢速版本:"
timeit slow_version
echo "优化版本:"
timeit fast_version
echo "最快版本:"
timeit faster_version
在日志分析场景中,这些优化可以带来数量级的性能提升:
bash复制# 实际案例:高效统计CSV数据
analyze_csv() {
local csvfile="$1"
local tmpfile=$(mktemp)
# 使用awk替代多次grep
awk -F, '
NR==1 { for(i=1;i<=NF;i++) header[$i]=i }
$header["status"] == "500" { print $header["ip"], $header["timestamp"] }
' "$csvfile" > "$tmpfile"
# 并行处理
local cores=$(nproc)
split -n "l/$cores" "$tmpfile" "part."
for part in part.*; do
(
declare -A ip_stats
while read -r ip timestamp; do
((ip_stats["$ip"]++))
done < "$part"
for ip in "${!ip_stats[@]}"; do
echo "$ip ${ip_stats[$ip]}"
done > "$part.result"
) &
done
wait
# 合并结果
cat *.result | awk '
{ stats[$1] += $2 }
END { for(ip in stats) print ip, stats[ip] }
' | sort -nrk2 | head -20
rm -f part.* *.result "$tmpfile"
}
4.3 跨平台兼容性实践
确保脚本在不同Unix-like系统上可移植:
bash复制#!/bin/bash
# 环境检测
case "$(uname -s)" in
Linux) platform="linux" ;;
Darwin) platform="mac" ;;
*) echo "Unsupported OS"; exit 1 ;;
esac
# 工具兼容层
if [[ "$platform" == "linux" ]]; then
SED="sed -i"
elif [[ "$platform" == "mac" ]]; then
SED="sed -i ''"
fi
# 路径标准化
normalize_path() {
local path="$1"
if [[ "$platform" == "mac" ]]; then
path=$(grealpath -m "$path")
else
path=$(realpath -m "$path")
fi
echo "$path"
}
# 实际案例:跨平台服务控制
service_control() {
local action="$1"
local service="$2"
case "$platform" in
linux)
systemctl "$action" "$service"
;;
mac)
brew services "$action" "$service"
;;
esac
}
在混合环境管理场景中,这种设计模式特别重要:
bash复制# 实际案例:统一部署脚本
deploy() {
local target="$1"
local build_dir=$(normalize_path "./build")
case "$platform" in
linux)
rsync -az "$build_dir/" "user@$target:/opt/app"
ssh "user@$target" "sudo systemctl restart app"
;;
mac)
scp -r "$build_dir/" "user@$target:/Applications/app.app"
ssh "user@$target" "launchctl unload /Library/LaunchDaemons/app.plist"
ssh "user@$target" "launchctl load /Library/LaunchDaemons/app.plist"
;;
esac
echo "部署成功到 $target"
}
5. Shell脚本调试与错误处理
5.1 调试技术与工具链
专业的调试方法可以快速定位问题:
bash复制#!/bin/bash
# 调试模式开关
DEBUG=${DEBUG:-0}
(( DEBUG )) && set -x
# 严格模式(推荐)
set -euo pipefail
IFS=$'\n\t'
# 自定义调试输出
debug() {
(( DEBUG )) && echo "[DEBUG] $*" >&2
}
# 函数调用追踪
trace() {
echo "+ $*" >&2
"$@"
}
# 实际使用
debug "Starting processing at $(date)"
trace cp -v config.ini /etc/app/
在复杂脚本中,这种调试基础设施可以大幅提升排错效率:
bash复制# 实际案例:带调试的数据库备份
backup_database() {
local db="$1"
local dumpfile="/backups/${db}_$(date +%Y%m%d).sql"
debug "Starting backup of $db to $dumpfile"
if ! mysqldump -u root "$db" > "$dumpfile"; then
echo "备份失败: $db" >&2
return 1
fi
local size=$(du -h "$dumpfile" | cut -f1)
debug "备份完成: $dumpfile ($size)"
}
5.2 错误处理与日志系统
完善的错误处理是生产级脚本的标志:
bash复制#!/bin/bash
# 日志初始化
LOG_FILE="${0%.*}.log"
exec 3>&1 # 保存原始stdout
exec > >(tee -a "$LOG_FILE") 2>&1
# 错误处理函数
error_exit() {
echo "[$(date +'%Y-%m-%d %H:%M:%S')] ERROR: $1" >&2
exit 1
}
# 带重试的命令执行
retry() {
local max=$1
local delay=$2
shift 2
for ((i=1; i<=max; i++)); do
if "$@"; then
return 0
fi
(( i < max )) && sleep "$delay"
done
error_exit "Command failed after $max attempts: $*"
}
# 实际使用
retry 3 5 curl -fsSL https://example.com/install.sh | bash
在自动化部署场景中,这种健壮的错误处理特别关键:
bash复制# 实际案例:集群配置验证
validate_cluster() {
local nodes=("node01" "node02" "node03")
local errors=0
for node in "${nodes[@]}"; do
if ! check_node "$node"; then
echo "[ERROR] $node 验证失败" >&2
((errors++))
continue
fi
if ! sync_config "$node"; then
echo "[ERROR] $node 配置同步失败" >&2
((errors++))
fi
done
(( errors > 0 )) && error_exit "集群验证发现 $errors 个错误"
echo "集群验证通过"
}
5.3 安全编程实践
脚本安全往往被忽视,却至关重要:
bash复制#!/bin/bash
# 安全防护措施
set -o noclobber # 防止意外覆盖文件
shopt -s failglob # 模式匹配失败时报错
# 安全目录操作
safe_cd() {
local dir="$1"
[[ -d "$dir" ]] || { echo "目录不存在: $dir" >&2; return 1; }
cd "$dir" || { echo "无法进入目录: $dir" >&2; return 1; }
}
# 安全文件处理
safe_write() {
local file="$1"
local content="$2"
[[ -e "$file" ]] && { echo "文件已存在: $file" >&2; return 1; }
umask 077
cat > "$file" <<< "$content"
}
# 实际案例:安全凭证处理
store_credential() {
local key="$1"
local value="$2"
local cred_file="/etc/secure/credentials"
# 加密存储
if command -v openssl >/dev/null; then
echo "$key=$(openssl enc -aes-256-cbc -pbkdf2 -a -salt -pass pass:${SECRET_KEY})" >> "$cred_file"
else
error_exit "OpenSSL不可用,无法安全存储凭证"
fi
}
在处理敏感操作时,这些安全措施可以避免灾难性后果:
bash复制# 实际案例:带防护的数据库操作
execute_sql() {
local db="$1"
local query="$2"
# 查询验证
[[ "$query" =~ ^(SELECT|SHOW) ]] || {
echo "拒绝非查询语句: ${query:0:30}..." >&2
return 1
}
# 使用参数化查询
mysql --batch --skip-column-names -e "$query" "$db" | while read -r result; do
echo "结果: $result"
done
}
6. 现代Shell脚本开发进阶
6.1 与Python/Go的混合编程
当Shell能力不足时,可以与其他语言协同:
bash复制#!/bin/bash
# 生成JSON配置(比手动拼接更安全)
generate_config() {
local port="$1"
local workers="$2"
python3 -c "import json; print(json.dumps({
'host': '0.0.0.0',
'port': $port,
'workers': $workers,
'debug': False
}))" > config.json
}
# 调用Go工具处理复杂计算
calculate_stats() {
local input="$1"
local output="$2"
if ! go run stats.go < "$input" > "$output"; then
echo "统计计算失败" >&2
return 1
fi
}
# 实际案例:混合性能监控
monitor_system() {
# Shell收集基础数据
local cpu_usage=$(top -bn1 | grep "Cpu(s)" | awk '{print $2 + $4}')
local mem_free=$(free -m | awk '/Mem:/ {print $4}')
# Python生成报告
python3 -c "
import datetime
print(f'''系统状态报告
时间: {datetime.datetime.now()}
CPU使用率: ${cpu_usage}%
可用内存: ${mem_free}MB
''')"
}
6.2 自动化测试框架
为关键脚本编写测试用例:
bash复制#!/bin/bash
# 简易测试框架
TEST_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$TEST_DIR/../lib/utils.sh"
setup() {
TEST_TMPDIR=$(mktemp -d)
}
teardown() {
rm -rf "$TEST_TMPDIR"
}
assert_equal() {
local expected="$1"
local actual="$2"
local message="${3:-}"
if [[ "$expected" != "$actual" ]]; then
echo "断言失败: $message (期望: '$expected', 实际: '$actual')" >&2
exit 1
fi
}
# 测试用例
test_string_trim() {
local result=$(trim " hello ")
assert_equal "hello" "$result" "字符串修剪"
}
# 运行测试
(setup; test_string_trim; teardown)
echo "所有测试通过"
在持续集成环境中,这种测试框架特别有用:
bash复制# 实际案例:CI中的脚本测试
run_tests() {
local test_files=(tests/*_test.sh)
local failures=0
for test_file in "${test_files[@]}"; do
echo "运行测试: $test_file"
if ! bash "$test_file"; then
((failures++))
fi
done
(( failures > 0 )) && error_exit "$failures 个测试失败"
echo "所有测试通过"
}
6.3 性能敏感场景优化
当处理大规模数据时需要特殊技巧:
bash复制#!/bin/bash
# 使用coproc实现并发控制
MAX_WORKERS=4
task_queue="/tmp/tasks.$$"
# 初始化任务队列
generate_tasks > "$task_queue"
# 启动worker池
for ((i=1; i<=MAX_WORKERS; i++)); do
coproc "worker$i" {
while read -r task; do
process_task "$task"
done
}
done
# 分配任务
for ((i=1; i<=MAX_WORKERS; i++)); do
cat "$task_queue" >&"${worker$i[1]}"
done
# 等待完成
for ((i=1; i<=MAX_WORKERS; i++)); do
wait "$worker$i_PID"
done
rm "$task_queue"
在日志分析等CPU密集型任务中,这种模式可以充分利用多核:
bash复制# 实际案例:并行日志处理
analyze_logs() {
local log_dir="$1"
local output="$2"
local tmpdir=$(mktemp -d)
# 分割日志文件
find "$log_dir" -name '*.log' | parallel --jobs 4 '
outfile="'$tmpdir'/result_{#}.txt"
grep "ERROR" {} | awk "{print \$1}" | sort | uniq -c > "$outfile"
'
# 合并结果
find "$tmpdir" -name 'result_*.txt' -exec cat {} + | sort | uniq > "$output"
rm -rf "$tmpdir"
}
7. Shell脚本工程化实践
7.1 项目结构与代码组织
专业级的Shell项目需要良好的结构:
code复制/my-shell-project
├── bin/ # 可执行脚本
│ ├── deploy
│ └── backup
├── lib/ # 公共函数库
│ ├── utils.sh
│ └── logging.sh
├── config/ # 配置文件
│ ├── production.conf
│ └── development.conf
├── tests/ # 测试用例
│ ├── utils_test.sh
│ └── integration.sh
├── docs/ # 文档
│ └── API.md
└── Makefile # 构建管理
典型的模块加载方式:
bash复制#!/bin/bash
# 加载库文件
for lib in lib/*.sh; do
source "$lib" || { echo "无法加载库: $lib"; exit 1; }
done
# 加载配置
CONFIG_FILE="config/${ENVIRONMENT:-production}.conf"
[[ -f "$CONFIG_FILE" ]] && source "$CONFIG_FILE"
7.2 文档生成与帮助系统
自动化文档可以提升脚本可用性:
bash复制#!/bin/bash
# 内嵌文档规范
:<<'DOC'
Usage: deploy.sh [options] <environment>
Options:
-v, --verbose 显示详细输出
-f, --force 强制部署,忽略警告
-h, --help 显示此帮助
Environment:
production 生产环境
staging 预发布环境
DOC
# 自动生成帮助
show_help() {
sed -n '/^:<<'\''DOC''$/,/^DOC$/ { /DOC$/d; p }' "$0"
}
# 实际案例:命令行解析
parse_args() {
while (( $# )); do
case "$1" in
-v|--verbose) VERBOSE=1 ;;
-f|--force) FORCE=1 ;;
-h|--help) show_help; exit 0 ;;
*) ENVIRONMENT="$1" ;;
esac
shift
done
[[ -z "$ENVIRONMENT" ]] && { show_help; exit 1; }
}
7.3 持续集成与交付
为Shell项目配置CI/CD流水线:
bash复制# .gitlab-ci.yml 示例
stages:
- test
- deploy
shellcheck:
stage: test
image: alpine
script:
- apk add shellcheck
- shellcheck bin/*.sh lib/*.sh
unit_test:
stage: test
image: ubuntu:24.04
script:
- apt-get update && apt-get install -y bats
- bats tests/
deploy_prod:
stage: deploy
image: ubuntu:24.04
only:
- main
script:
- bin/deploy production
在团队协作中,这种自动化流程可以确保代码质量:
bash复制# 实际案例:预提交钩子
#!/bin/bash
# .git/hooks/pre-commit
# 运行测试
if ! make test; then
echo "测试失败,提交中止" >&2
exit 1
fi
# 检查代码风格
if ! shfmt -d .; then
echo "代码风格检查失败" >&2
exit 1
fi
# 验证文档
if ! markdownlint docs/; then
echo "文档验证失败" >&2
exit 1
fi
8. 真实案例:Ubuntu 24.04系统初始化脚本
8.1 需求分析与设计
典型的新系统初始化需求:
- 基础软件包安装
- 安全加固(防火墙、SSH配置)
- 用户与权限设置
- 监控代理部署
- 自定义配置应用
bash复制#!/bin/bash
# 初始化脚本框架
set -euo pipefail
IFS=$'\n\t'
# 配置加载
CONFIG_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/config"
source "${CONFIG_DIR}/init.conf"
# 日志初始化
LOG_FILE="/var/log/system_init.log"
exec 3>&1
exec > >(tee -a "$LOG_FILE") 2>&1
# 主流程
main() {
echo "[$(date)] 开始系统初始化"
setup_packages
secure_system
create_users
deploy_monitoring
apply_custom_config
echo "[$(date)] 初始化完成"
echo "详细信息请查看日志: $LOG_FILE" >&3
}
8.2 核心模块实现
软件包管理模块示例:
bash复制setup_packages() {
local base_pkgs=(
"curl" "wget" "git" "htop" "iotop"
"sysstat" "unzip" "jq" "make" "gcc"
)
# Ubuntu 24.04特有配置
if grep -q 'Ubuntu 24.04' /etc/os-release; then
base_pkgs+=( "linux-tools-generic" "python3-venv" )
fi
echo "更新软件包索引..."
apt-get update
echo "安装基础软件包..."
DEBIAN_FRONTEND=noninteractive apt-get install -y "${base_pkgs[@]}"
# 可选组件
if [[ "$INSTALL_DOCKER" == "true" ]]; then
install_docker
fi
}
install_docker() {
if ! command -v docker &>/dev/null; then
echo "安装Docker..."
apt-get install -y ca-certificates curl gnupg
install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" > /etc/apt/sources.list.d/docker.list
apt-get update
apt-get install -y docker-ce docker-ce-cli containerd.io
fi
}
8.3 安全加固实现
系统安全模块示例:
bash复制secure_system() {
echo "配置防火墙..."
ufw allow ssh
ufw --force enable
echo "加固SSH配置..."
sed -i 's/^#PermitRootLogin.*/PermitRootLogin no/' /etc/ssh/sshd_config
sed -i 's/^PasswordAuthentication.*/PasswordAuthentication no/' /etc/ssh/sshd_config
echo "AllowUsers ${ADMIN_USER}" >> /etc/ssh/sshd_config
systemctl restart sshd
echo "配置自动安全更新..."
apt-get install -y unattended-upgrades
dpkg-reconfigure -plow unattended-upgrades
echo "设置文件权限..."
chmod 600 /etc/shadow
chmod 644 /etc/passwd
}
8.4 部署与验证
监控部署模块示例:
bash复制deploy_monitoring() {
[[ "$DEPLOY_MONITORING" != "true" ]] && return
echo "部署监控代理..."
local tmpdir=$(mktemp -d)
curl -sSL "$MONITORING_AGENT_URL" -o "$tmpdir/agent.deb"
dpkg -i "$tmpdir/agent.deb"
echo "配置监控代理..."
cat > /etc/agent.conf <<EOF
{
"server": "$MONITORING_SERVER",
"interval": 60,
"tags": ["env:$ENVIRONMENT"]
}
EOF
systemctl enable --now agent
rm -rf "$tmpdir"
echo "验证监控连接..."
if ! timeout 10 curl -fs http://localhost:9100/metrics >/dev/null; then
echo "监控代理启动失败" >&2
return 1
fi
}
9. Shell脚本资源与进阶学习
9.1 权威参考资料推荐
-
官方文档
- Bash参考手册:
info bash或在线版 - Ubuntu 24.04 Bash手册页:
man bash - POSIX Shell规范:IEEE Std 1003.1
- Bash参考手册:
-
经典书籍
- 《Shell脚本学习指南》(Arnold Robbins)
- 《Bash高级脚本编程指南》(Mendel Cooper)
- 《Linux命令行与Shell脚本编程大全》
-
在线资源
- Bash Hackers Wiki
- Google Shell风格指南
- Ubuntu官方Bash教程
9.2 实用工具链
-
代码质量工具
- ShellCheck:静态分析工具
- shfmt:Shell脚本格式化工具
- bats:Bash自动化测试框架
-
开发辅助工具
- bashdb:Bash调试器
- entr:文件变更监视器
- parallel:GNU并行工具
-
性能分析工具
- time:测量命令执行时间
- strace:系统调用跟踪
- perf:Linux性能分析器
9.3 常见问题解决方案
-
编码问题
bash复制# 确保脚本以UTF-8运行 export LANG=en_US.UTF-8 export LC_ALL=en_US.UTF-8 -
路径问题
bash复制# 获取脚本所在目录(兼容各种调用方式) SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -
兼容性问题
bash复制# 检查Bash版本 (( BASH_VERSINFO[0] < 4 )) && echo "需要Bash 4.0+" >&2 && exit 1 -
**临时文件
