1. Shell编程入门:从简洁到优雅的实战指南
作为Linux系统管理员最趁手的工具,Shell脚本在日常工作中扮演着不可替代的角色。记得我刚入行时,面对一堆需要重复执行的命令手足无措,直到发现Shell脚本这个"自动化神器"。今天分享的这套实例合集,正是我这些年来积累的精华案例,特别适合已经掌握基础语法但缺乏实战经验的朋友。
与传统的语法教程不同,这套实例采用"问题驱动"的设计思路。每个案例都源自真实的运维场景,从最简单的文件备份到复杂的日志分析,你会看到如何用20行代码解决过去需要手动操作半小时的任务。更重要的是,这些脚本都遵循KISS原则(Keep It Simple and Stupid)——没有炫技式的复杂语法,只有最直接的问题解决思路。
2. 环境准备与基础规范
2.1 开发环境配置
工欲善其事,必先利其器。推荐使用VS Code作为主要编辑器,安装ShellCheck插件实时检查语法错误。这个组合在我处理生产环境脚本时帮了大忙——有次差点因为一个遗漏的空格导致整晚的批量处理失败,是ShellCheck的红线提示救了我。
bash复制# 安装基础工具(适用于Debian/Ubuntu)
sudo apt update && sudo apt install -y shellcheck tree
# 验证bash版本(建议4.0+)
bash --version | head -n1
注意:生产环境脚本务必在开头使用
#!/usr/bin/env bash而非直接#!/bin/bash,这能更好地处理不同系统的路径差异。这是我用两次凌晨故障换来的经验。
2.2 脚本规范要点
好的脚本应该像说明书一样易读。这是我的团队强制执行的标准:
- 文件名全小写,用连字符分隔(如
backup-mysql.sh) - 函数使用小驼峰命名法(
calculateSize()) - 全局变量全大写加下划线(
MAX_RETRY=3) - 每个函数前必须有注释说明用途和参数
bash复制#!/usr/bin/env bash
# 用途:MySQL数据库备份脚本
# 作者:YourName
# 修改日期:2023-07-20
CONFIG_FILE="/etc/db.conf"
LOG_DIR="/var/log/backup"
# 加载数据库配置
loadConfig() {
[ ! -f "$CONFIG_FILE" ] && echo "Config missing" && exit 1
source "$CONFIG_FILE"
}
3. 经典实例解析
3.1 文件批量处理模板
这是我最常被问到的场景:如何批量重命名/转换/处理一批文件?下面这个模板已经帮我处理过上万张图片和日志文件:
bash复制#!/usr/bin/env bash
# 批量将JPG转换为WebP格式
INPUT_DIR="$1"
OUTPUT_DIR="${INPUT_DIR}_webp"
[ -z "$INPUT_DIR" ] && echo "Usage: $0 <input_dir>" && exit 1
mkdir -p "$OUTPUT_DIR"
convertToWebp() {
local input_file="$1"
local filename=$(basename "$input_file" .jpg)
cwebp -q 80 "$input_file" -o "${OUTPUT_DIR}/${filename}.webp"
}
export -f convertToWebp
find "$INPUT_DIR" -type f -name "*.jpg" | parallel convertToWebp
关键技巧:
- 使用
parallel命令实现多进程并发(处理速度提升5-8倍) export -f将函数传递到子进程- 变量用
${}明确边界,避免歧义
3.2 系统监控报警脚本
这个脚本曾帮我及时发现三次服务器异常,核心是awk的巧妙运用:
bash复制#!/usr/bin/env bash
# 监控CPU/内存/磁盘并发送报警
THRESHOLD_CPU=80
THRESHOLD_MEM=90
THRESHOLD_DISK=85
checkSystem() {
local cpu_usage=$(top -bn1 | awk '/^%Cpu/{print 100-$8}')
local mem_usage=$(free | awk '/Mem/{printf "%.0f", $3/$2*100}')
local disk_usage=$(df -h / | awk 'NR==2{print $5}' | tr -d '%')
(( $(echo "$cpu_usage > $THRESHOLD_CPU" | bc -l) )) && \
sendAlert "CPU" "$cpu_usage"
[ "$mem_usage" -gt "$THRESHOLD_MEM" ] && \
sendAlert "Memory" "$mem_usage"
[ "$disk_usage" -gt "$THRESHOLD_DISK" ] && \
sendAlert "Disk" "$disk_usage"
}
sendAlert() {
local metric=$1
local value=$2
echo "[$(date)] WARNING: $metric usage ${value}%" >> /var/log/system_monitor.log
# 实际环境中这里可以接入邮件/短信报警
}
4. 高级技巧实战
4.1 参数解析最佳实践
很多新手直接用$1、$2处理参数,这在复杂脚本中会成为维护噩梦。这是我总结的模板:
bash复制#!/usr/bin/env bash
# 支持长选项的参数解析模板
usage() {
cat <<EOF
Usage: $0 [OPTIONS]
-h, --help Show this help
-v, --verbose Enable debug output
-f, --file FILE Target file path
EOF
exit 1
}
parseArgs() {
while [ "$#" -gt 0 ]; do
case "$1" in
-h|--help) usage ;;
-v|--verbose) VERBOSE=1 ;;
-f|--file)
[ -z "$2" ] && echo "File path missing" && usage
TARGET_FILE="$2"
shift
;;
*) echo "Unknown option: $1" && usage ;;
esac
shift
done
}
main() {
parseArgs "$@"
[ -z "$TARGET_FILE" ] && echo "File required" && usage
echo "Processing $TARGET_FILE..."
# 业务逻辑
}
4.2 错误处理机制
没有健全的错误处理是脚本最大的安全隐患。这套机制应该成为你的肌肉记忆:
bash复制#!/usr/bin/env bash
# 错误处理模板
set -euo pipefail # 开启严格模式
trap 'cleanup $? $LINENO' EXIT
cleanup() {
local exit_code=$1
local line_no=$2
if [ "$exit_code" -ne 0 ]; then
echo "[ERROR] Failed at line $line_no: $BASH_COMMAND"
# 发送报警/记录日志
fi
# 清理临时文件等资源
rm -rf "$TMP_DIR"
}
main() {
local TMP_DIR=$(mktemp -d)
# 业务逻辑
processData || {
echo "Data processing failed"
return 1
}
}
关键点:
set -euo pipefail是安全脚本的黄金标准trap确保任何情况下都能执行清理- 使用
||处理可预见的错误
5. 性能优化实战
5.1 避免常见性能陷阱
这个日志分析脚本的优化过程很有代表性:
bash复制# 原始版本(处理100MB日志需要58秒)
analyzeLog() {
while read -r line; do
if [[ "$line" =~ "ERROR" ]]; then
echo "$line" >> errors.log
fi
done < system.log
}
# 优化版本(同样日志仅需3秒)
optimizedAnalyze() {
grep "ERROR" system.log > errors.log
awk '{print $1}' errors.log | sort | uniq -c > error_stats.txt
}
优化原则:
- 能用内置命令就别用循环
- 减少管道数量(每个
|都会创建子进程) - 提前过滤减少处理量
5.2 并行处理模式
当需要处理大量数据时,这个模板能大幅提升效率:
bash复制#!/usr/bin/env bash
# 并行处理CSV文件
processRecord() {
local record="$1"
# 模拟耗时操作
sleep 0.1
echo "${record^^}" # 转为大写
}
export -f processRecord
main() {
local input_file="$1"
local worker_count=${2:-4}
# 创建命名管道
local fifo=$(mktemp -u)
mkfifo "$fifo"
# 启动消费者进程
for ((i=0; i<worker_count; i++)); do
while read -r line; do
processRecord "$line"
done < "$fifo" &
done
# 生产者写入数据
awk 'NR>1' "$input_file" > "$fifo"
wait
rm "$fifo"
}
6. 调试与排错指南
6.1 调试技巧合集
这些命令是我每天都会用到的调试利器:
bash复制# 显示执行命令(xtrace)
set -x
your_script.sh
set +x
# 检查语法而不执行
bash -n script.sh
# 显示变量替换过程
bash -v script.sh
# 使用DEBUG陷阱
trap 'echo "Line $LINENO: var=$var"' DEBUG
6.2 常见错误速查表
| 错误现象 | 可能原因 | 解决方案 |
|---|---|---|
脚本执行报错command not found |
1. 命令拼写错误 2. PATH环境变量问题 |
1. 使用type -a cmd验证命令2. 脚本开头设置 PATH |
| 变量值为空 | 1. 未初始化 2. 作用域问题 |
1. 使用${var:-default}语法2. 检查是否在子shell中修改 |
| 权限拒绝 | 1. 脚本没有执行权限 2. 目标文件不可写 |
1. chmod +x script.sh2. 使用 sudo或修改权限 |
| 参数解析异常 | 1. 引号缺失 2. 特殊字符未转义 |
1. 用"$@"代替$*2. 使用 getopts替代手工解析 |
7. 脚本安全强化
7.1 输入验证模板
所有用户输入都应该视为有害的。这个验证框架值得放入你的代码库:
bash复制#!/usr/bin/env bash
# 安全的输入验证模板
validateInput() {
local input="$1"
# 非空检查
[ -z "$input" ] && die "Input cannot be empty"
# 数字检查
[[ "$input" =~ ^[0-9]+$ ]] || die "Numeric input required"
# 路径安全检测
[[ "$input" =~ \.\. ]] && die "Path traversal attempt detected"
# 自定义规则
[ "${#input}" -gt 32 ] && die "Input too long"
}
die() {
local msg="$1"
echo "[ERROR] $msg" >&2
exit 1
}
main() {
read -rp "Enter user ID: " user_id
validateInput "$user_id"
# 安全使用输入
grep "^$user_id:" /etc/passwd
}
7.2 权限控制策略
生产环境脚本必须考虑最小权限原则:
bash复制#!/usr/bin/env bash
# 权限控制示例
checkPrivileges() {
# 如果是root则降权
if [ "$(id -u)" -eq 0 ]; then
echo "Running as root, downgrading privileges..."
exec sudo -u nobody "$0" "$@"
fi
# 检查关键目录权限
[ -w "/var/log/app" ] || \
die "No write permission to log directory"
}
setupSandbox() {
# 创建安全沙箱
local sandbox=$(mktemp -d)
chmod 700 "$sandbox"
cd "$sandbox" || die "Cannot enter sandbox"
# 设置资源限制
ulimit -n 1024 # 文件描述符
ulimit -u 100 # 进程数
}
8. 项目结构与模块化
8.1 大型脚本组织规范
当脚本超过300行时,就该考虑模块化了。这是我的项目结构模板:
code复制/opt/scripts/
├── lib/ # 公共函数库
│ ├── utils.sh
│ └── logging.sh
├── config.d/ # 配置文件
│ └── app.conf
├── modules/ # 功能模块
│ ├── db.sh
│ └── network.sh
└── main.sh # 主入口
主脚本通过source加载模块:
bash复制#!/usr/bin/env bash
# main.sh
SCRIPT_DIR=$(dirname "$(readlink -f "$0")")
source "${SCRIPT_DIR}/lib/utils.sh"
source "${SCRIPT_DIR}/lib/logging.sh"
source "${SCRIPT_DIR}/modules/db.sh"
loadConfig "${SCRIPT_DIR}/config.d/app.conf"
8.2 函数库开发技巧
可复用的函数库应该遵循这些原则:
bash复制#!/usr/bin/env bash
# lib/string.sh
# 判断字符串是否包含子串
stringContains() {
local str="$1"
local substr="$2"
[[ "$str" == *"$substr"* ]]
}
# 生成随机字符串
randomString() {
local length=${1:-16}
tr -dc 'a-zA-Z0-9' < /dev/urandom | head -c "$length"
}
# 仅当直接执行时运行测试代码
if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then
stringContains "hello world" "world" && echo "Test passed"
echo "Random: $(randomString 8)"
fi
9. 跨平台兼容方案
9.1 系统差异处理
这段代码帮我解决了CentOS和Ubuntu的兼容问题:
bash复制#!/usr/bin/env bash
# 系统兼容层
case "$(uname -s)" in
Linux)
if [ -f /etc/redhat-release ]; then
# CentOS/RHEL
INSTALL_CMD="yum install -y"
elif [ -f /etc/debian_version ]; then
# Debian/Ubuntu
INSTALL_CMD="apt-get install -y"
fi
;;
Darwin)
# macOS
INSTALL_CMD="brew install"
;;
*)
die "Unsupported OS"
;;
esac
# 统一安装函数
installPackage() {
local pkg="$1"
echo "Installing $pkg..."
if ! $INSTALL_CMD "$pkg"; then
echo "Failed to install $pkg, trying alternative..."
# 备用方案
fi
}
9.2 Bash版本适配
这个技巧确保脚本在旧版bash中也能运行:
bash复制#!/usr/bin/env bash
# 版本兼容处理
checkBashVersion() {
local required=4
local current=${BASH_VERSINFO[0]}
if [ "$current" -lt "$required" ]; then
echo "Requires Bash $required+, current is $current" >&2
# 尝试查找新版bash
if command -v bash5 >/dev/null; then
exec bash5 "$0" "$@"
else
exit 1
fi
fi
}
# 新语法封装为函数
associativeArray() {
local var_name=$1
local key=$2
local value=$3
if [ "${BASH_VERSINFO[0]}" -ge 4 ]; then
declare -gA "$var_name"
eval "$var_name[$key]='$value'"
else
# 旧版bash的替代方案
eval "${var_name}_${key}='$value'"
fi
}
10. 实战项目:自动化部署系统
10.1 项目架构设计
这个简易部署系统包含以下组件:
- 配置解析模块
- 文件同步引擎
- 前置/后置钩子
- 回滚机制
目录结构示例:
code复制/deploy-tool/
├── bin/ # 可执行脚本
│ └── deploy
├── hooks/ # 钩子脚本
│ ├── pre-deploy.sh
│ └── post-deploy.sh
└── config/
└── production.yml
10.2 核心实现代码
bash复制#!/usr/bin/env bash
# bin/deploy
loadConfig() {
local env=${1:-staging}
CONFIG_FILE="config/${env}.yml"
[ ! -f "$CONFIG_FILE" ] && die "Config not found: $CONFIG_FILE"
# 解析YAML(简化版)
SERVER_LIST=$(grep '^servers:' -A10 "$CONFIG_FILE" | awk '/^- /{print $2}')
DEPLOY_DIR=$(awk '/deploy_dir:/{print $2}' "$CONFIG_FILE")
}
runHook() {
local hook_type=$1
local hook_script="hooks/${hook_type}-${hook_name}.sh"
[ -x "$hook_script" ] || return 0
logInfo "Running $hook_type hook..."
if ! "$hook_script"; then
die "$hook_type hook failed"
fi
}
deployToServer() {
local server=$1
logInfo "Deploying to $server"
rsync -az --delete \
--exclude='.git' \
--exclude='*.swp' \
./ "$server:$DEPLOY_DIR"
}
main() {
loadConfig "$@"
runHook "pre"
for server in $SERVER_LIST; do
deployToServer "$server"
done
runHook "post"
}
11. 性能敏感型脚本优化
11.1 避免子shell开销
这个对比展示了如何提升10倍性能:
bash复制# 慢速版本(频繁创建子shell)
sum=0
while read -r line; do
((sum+=line))
done < <(seq 100000)
# 快速版本(纯bash实现)
sum=0
for ((i=1; i<=100000; i++)); do
((sum+=i))
done
优化要点:
- 避免在循环中调用外部命令
- 用bash内置算术替代
expr或bc - 减少管道和子shell的使用
11.2 内存高效处理
处理大文件时的内存优化技巧:
bash复制#!/usr/bin/env bash
# 流式处理大文件(固定内存占用)
processLargeFile() {
local input_file="$1"
local chunk_size=10000
while IFS= read -r -d '' line; do
echo "Processing: ${line::100}..." # 只取前100字符
((count++))
if ((count % chunk_size == 0)); then
echo "Processed $count lines" >&2
fi
done < <(tr '\n' '\0' < "$input_file")
}
12. 代码质量保障
12.1 静态检查集成
这是我的CI流水线配置示例:
bash复制#!/usr/bin/env bash
# ci/run-checks.sh
set -e
# 语法检查
find . -name '*.sh' | while read -r file; do
echo "Checking $file"
shellcheck -x "$file"
bash -n "$file"
done
# 单元测试
(
cd test || exit
for test_script in *_test.sh; do
bash "$test_script"
done
)
# 覆盖率检查(需要bashcov)
if command -v bashcov >/dev/null; then
bashcov --skip-uncovered test/run_all.sh
fi
12.2 测试框架示例
基于bash的简易测试框架:
bash复制#!/usr/bin/env bash
# test/string_test.sh
source ../lib/string.sh
testStringContains() {
stringContains "hello" "ell" || return 1
! stringContains "hello" "world" || return 1
}
testRandomString() {
local str1=$(randomString 10)
local str2=$(randomString 10)
[ "${#str1}" -eq 10 ] || return 1
[ "$str1" != "$str2" ] || return 1
}
# 运行所有测试
declare -F | awk -F'[ ]' '/test.*/{print $3}' | while read -r test; do
if "$test"; then
echo "PASS: $test"
else
echo "FAIL: $test"
exit 1
fi
done
13. 文档与帮助系统
13.1 自动化帮助生成
这个模板让脚本自带漂亮的帮助文档:
bash复制#!/usr/bin/env bash
# 自动生成帮助文档
usage() {
cat <<EOF | less
$(basename "$0") - 自动化部署工具
$(printBanner "SYNOPSIS")
$0 [OPTIONS] COMMAND [ARGS...]
$(printBanner "COMMANDS")
deploy 部署应用到环境
--env=ENV 指定环境(staging|production)
--dry-run 试运行
rollback 回滚到上一版本
--confirm 跳过确认提示
$(printBanner "EXAMPLES")
# 部署到预发布环境
$0 deploy --env=staging
# 生产环境回滚
$0 rollback --confirm
EOF
exit 0
}
printBanner() {
printf "\n\033[1m%s\033[0m\n" "$1"
}
13.2 Markdown文档生成
将脚本帮助输出转为Markdown:
bash复制#!/usr/bin/env bash
# 生成README.md
generateDocs() {
local output="README.md"
cat <<EOF > "$output"
# $(basename "$(pwd)")
## 功能概述
$(./script.sh --help | sed -n '/SYNOPSIS/,/EXAMPLES/p' | sed '1d;$d')
## 使用示例
\`\`\`bash
$(./script.sh --help | sed -n '/EXAMPLES/,$p' | sed '1d')
\`\`\`
EOF
}
14. 交互式脚本设计
14.1 用户界面最佳实践
这个模板包含了交互式脚本的所有要素:
bash复制#!/usr/bin/env bash
# 交互式配置生成器
showMenu() {
clear
echo "$(tput setaf 4)=== 系统配置工具 ===$(tput sgr0)"
echo "1. 网络配置"
echo "2. 安全设置"
echo "3. 服务管理"
echo "0. 退出"
echo
read -rp "请选择 [0-3]: " choice
case "$choice" in
1) networkMenu ;;
2) securityMenu ;;
3) serviceMenu ;;
0) exit 0 ;;
*) showMenu ;;
esac
}
networkMenu() {
clear
echo "$(tput setaf 3)=== 网络配置 ===$(tput sgr0)"
current_ip=$(hostname -I | awk '{print $1}')
echo "当前IP: $current_ip"
read -rp "新IP地址 (留空保持当前): " new_ip
[ -n "$new_ip" ] && changeIP "$new_ip"
}
changeIP() {
local ip="$1"
if ! [[ "$ip" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "无效IP格式"
sleep 1
return
fi
echo "正在应用新IP: $ip..."
# 实际配置逻辑
sleep 2
}
14.2 进度显示技巧
长时间操作需要良好的进度反馈:
bash复制#!/usr/bin/env bash
# 进度条实现
progressBar() {
local duration=${1:-10}
local width=50
local increment=$((duration * 100 / width / 100))
for ((i=0; i<=width; i++)); do
percentage=$((i * 100 / width))
printf "\r[%-${width}s] %d%%" \
"$(printf '#%.0s' $(seq 1 $i))" \
"$percentage"
sleep "$increment"
done
echo
}
spinCursor() {
local pid=$1
local spin='-\|/'
local i=0
while kill -0 "$pid" 2>/dev/null; do
i=$(( (i+1) %4 ))
printf "\r处理中... %s" "${spin:$i:1}"
sleep 0.1
done
printf "\r%s\n" "处理完成"
}
# 使用示例
longRunningTask() {
sleep 5 &
spinCursor $!
}
15. 实用代码片段库
15.1 日期时间处理
这些日期操作覆盖90%的日常需求:
bash复制#!/usr/bin/env bash
# 日期时间工具函数
# 获取当前时间戳
timestamp() {
date +%s
}
# 格式化日期
formatDate() {
local fmt=${1:-"%Y-%m-%d %H:%M:%S"}
date +"$fmt"
}
# 计算日期差
dateDiff() {
local start="$1"
local end="$2"
local unit="${3:-days}"
local diff_seconds=$(($(date -d "$end" +%s) - $(date -d "$start" +%s)))
case "$unit" in
seconds) echo "$diff_seconds" ;;
minutes) echo "$((diff_seconds / 60))" ;;
hours) echo "$((diff_seconds / 3600))" ;;
days) echo "$((diff_seconds / 86400))" ;;
*) echo "Invalid unit: $unit" >&2; return 1 ;;
esac
}
# 测试示例
echo "当前时间: $(formatDate)"
echo "时间戳: $(timestamp)"
echo "距离元旦还有: $(dateDiff "$(formatDate)" "2024-01-01") 天"
15.2 文本处理大全
这些文本处理技巧能替代大多数简单awk/sed场景:
bash复制#!/usr/bin/env bash
# 文本处理工具集
# 提取JSON字段(简易版)
jsonGet() {
local json="$1"
local key="$2"
grep -o "\"$key\":\"[^\"]*\"" <<< "$json" | cut -d'"' -f4
}
# CSV转TSV
csvToTsv() {
local file="$1"
tr ',' '\t' < "$file"
}
# 生成随机密码
genPassword() {
local length=${1:-12}
LC_ALL=C tr -dc 'A-Za-z0-9!@#$%^&*()' < /dev/urandom | head -c "$length"
}
# 字符串填充
padString() {
local str="$1"
local len="$2"
local pad_char="${3:- }"
printf "%${len}s" "$str" | tr ' ' "$pad_char"
}
# 测试示例
echo "JSON解析: $(jsonGet '{"name":"John","age":30}' 'name')"
echo "随机密码: $(genPassword 16)"
echo "填充文本: $(padString "hello" 10 '-')"
16. 资源清理与管理
16.1 临时文件处理
这个模式确保脚本退出时清理所有临时资源:
bash复制#!/usr/bin/env bash
# 安全的临时文件管理
setupTemp() {
TEMP_DIR=$(mktemp -d)
TEMP_FILES=()
trap 'cleanup' EXIT INT TERM
}
createTempFile() {
local prefix=${1:-temp}
local temp_file=$(mktemp -p "$TEMP_DIR" "${prefix}.XXXXXX")
TEMP_FILES+=("$temp_file")
echo "$temp_file"
}
cleanup() {
for file in "${TEMP_FILES[@]}"; do
[ -f "$file" ] && rm -f "$file"
done
[ -d "$TEMP_DIR" ] && rm -rf "$TEMP_DIR"
}
main() {
setupTemp
log_file=$(createTempFile "log")
data_file=$(createTempFile "data")
echo "Working with $log_file and $data_file"
# 业务逻辑
}
16.2 进程管理技巧
后台进程管理是脚本稳定性的关键:
bash复制#!/usr/bin/env bash
# 可靠的进程管理
startWorker() {
local worker_id=$1
while true; do
processTask "$worker_id"
sleep 1
done
}
monitorWorkers() {
local worker_count=${1:-4}
local worker_pids=()
# 启动工作进程
for ((i=0; i<worker_count; i++)); do
startWorker "$i" &
worker_pids+=($!)
done
# 监控进程状态
while true; do
for pid in "${worker_pids[@]}"; do
if ! kill -0 "$pid" 2>/dev/null; then
echo "Worker $pid died, restarting..."
startWorker "$pid" &
worker_pids=("${worker_pids[@]/$pid/}" $!)
fi
done
sleep 5
done
}
17. 实战:日志分析系统
17.1 日志解析引擎
这个日志分析器曾帮我找出系统瓶颈:
bash复制#!/usr/bin/env bash
# 多维度日志分析
analyzeApacheLog() {
local log_file="$1"
local report_dir="$2"
# 状态码统计
awk '{print $9}' "$log_file" | sort | uniq -c \
> "$report_dir/status_codes.txt"
# 最频繁的URL
awk '{print $7}' "$log_file" | sort | uniq -c | sort -nr | head -20 \
> "$report_dir/top_urls.txt"
# 流量按小时分布
awk '{split($4,array,":"); print array[2]":00"}' "$log_file" \
| sort | uniq -c > "$report_dir/hourly_traffic.txt"
# 慢请求分析
awk '{if ($11>1000) print $11,$7}' "$log_file" | sort -nr \
> "$report_dir/slow_requests.txt"
}
generateReport() {
local report_dir="$1"
echo "# 日志分析报告 $(date)" > "$report_dir/report.md"
for section in status_codes top_urls hourly_traffic slow_requests; do
echo -e "\n## ${section//_/ }" >> "$report_dir/report.md"
cat "$report_dir/$section.txt" >> "$report_dir/report.md"
done
}
17.2 实时监控实现
这个技巧可以实时监控日志变化:
bash复制#!/usr/bin/env bash
# 实时日志监控
tailLog() {
local log_file="$1"
local filter="${2:-}"
tail -n0 -F "$log_file" | while read -r line; do
if [ -n "$filter" ] && ! grep -q "$filter" <<< "$line"; then
continue
fi
processLogLine "$line"
done
}
processLogLine() {
local line="$1"
# 错误检测
if grep -q "ERROR" <<< "$line"; then
echo "[ERROR] $(date '+%H:%M:%S') $line" >&2
sendAlert "$line"
fi
# 性能指标提取
if [[ "$line" =~ response_time=([0-9.]+)ms ]]; then
local rt="${BASH_REMATCH[1]}"
updateStats "response_time" "$rt"
fi
}
18. 扩展技巧与模式
18.1 插件系统实现
这个架构让脚本支持动态扩展:
bash复制#!/usr/bin/env bash
# 插件系统实现
PLUGIN_DIR="./plugins"
loadPlugins() {
local plugin_type="$1"
for plugin in "$PLUGIN_DIR"/*."$plugin_type".sh; do
[ -f "$plugin" ] || continue
source "$plugin"
echo "Loaded plugin: $plugin"
done
}
runHook() {
local hook_name="$1"
shift
for plugin_func in $(compgen -A function | grep "^${hook_name}_"); do
echo "Running hook: $plugin_func"
"$plugin_func" "$@"
done
}
# 插件示例:plugins/logger.debug.sh
debug_log() {
echo "[DEBUG] $1" >> debug.log
}
setup_debug() {
alias log='debug_log'
}
18.2 状态机模式
复杂流程的状态机实现:
bash复制#!/usr/bin/env bash
# 简单状态机实现
declare -A state_transitions=(
["START"]="CONFIG"
["CONFIG"]="DOWNLOAD,VERIFY"
["DOWNLOAD"]="VERIFY"
["VERIFY"]="INSTALL,FAIL"
["INSTALL"]="CLEANUP"
["CLEANUP"]="END"
["FAIL"]="END"
)
runStateMachine() {
local current_state="START"
while [ "$current_state" != "END" ]; do
case "$current_state" in
START)
echo "初始化系统..."
current_state="CONFIG"
;;
CONFIG)
loadConfig || current_state="FAIL"
current_state="DOWNLOAD"
;;
DOWNLOAD)
downloadFiles || current_state="FAIL"
current_state="VERIFY"
;;
VERIFY)
verifyIntegrity && current_state="INSTALL" || current_state="FAIL"
;;
INSTALL)
runInstallation || current_state="FAIL"
current_state="CLEANUP"
;;
CLEANUP)
cleanupResources
current_state="END"
;;
FAIL)
handleFailure
current_state="END"
;;
esac
done
}
19. 跨语言集成
19.1 调用Python代码
混合编程的实用模式:
bash复制#!/usr/bin/env bash
# 调用Python处理数据
processWithPython() {
local input_file="$1"
local output_file="$2"
python3 - <<EOF | tee "$output_file"
import json
import sys
data = {
'timestamp': $(date +%s),
'input_file': "$input_file",
'