# Bash内置调试
bash -n script.sh # 语法检查
bash -x script.sh # 执行追踪
bash -v script.sh # 详细模式
# 脚本内调试
set -x # 开启追踪
set +x # 关闭追踪
# DEBUG陷阱
trap 'echo "LINE:$LINENO CMD:$BASH_COMMAND"' DEBUG
# 调试函数
debug() {
[[ $DEBUG -eq 1 ]] && echo "[DEBUG] $*" >&2
}
DEBUG=1
debug "变量: var=$var"
# 安装
sudo apt install shellcheck
# 使用
shellcheck myscript.sh
shellcheck -S error myscript.sh # 仅错误
# 常见问题
# SC2086: 引用变量
echo $var # 错误
echo "$var" # 正确
# SC2155: 分开声明和赋值
local var=$(cmd) # 不推荐
local var; var=$(cmd) # 推荐
# 忽略检查
# shellcheck disable=SC2086
echo $SPECIAL_VAR
# 等号空格
name = "hello" # 错误!
name="hello" # 正确
# if缺少空格
if[$x -eq 1] # 错误!
if [[ $x -eq 1 ]] # 正确
# 未引用变量
file="my doc.txt"
rm $file # 删两个文件!
rm "$file" # 正确
# 管道子Shell变量不传回
count=0
cat file | while read line; do
((count++)) # 子Shell中修改
done
echo $count # 0!
# 修复: while read ... done < file
#!/bin/bash
set -euo pipefail
# trap捕获
cleanup() {
echo "清理临时文件..."
rm -f /tmp/myapp_$$.*
}
trap cleanup EXIT INT TERM
# 错误处理函数
error_handler() {
echo "错误: 第$1行" >&2
}
trap 'error_handler $LINENO' ERR
# 安全执行
safe_exec() {
local desc="$1"; shift
if eval "$@"; then
echo "✅ $desc"
else
echo "❌ $desc" >&2
return 1
fi
}
# 带重试
retry() {
local max="$1"; shift
local n=1
while [[ $n -le $max ]]; do
eval "$@" && return 0
echo "重试 $n/$max..."
((n++)); sleep 2
done
echo "重试失败" >&2; return 1
}
LOG="/tmp/$(basename "$0" .sh).log"
VERBOSE=0
log() {
local level="$1"; shift
local msg="[$(date '+%Y-%m-%d %H:%M:%S')] [$level] $*"
echo "$msg" >> "$LOG_FILE"
[[ "$VERBOSE" -eq 1 || "$level" != "DEBUG" ]] && echo "$msg"
}
info() { log "INFO" "$@"; }
warn() { log "WARN" "$@"; }
error() { log "ERROR" "$@" >&2; }
info "脚本开始"
warn "磁盘不足"
error "连接失败"
#!/bin/bash
set -euo pipefail
readonly SCRIPT_NAME=$(basename "$0")
readonly SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd)
readonly LOG_FILE="/tmp/${SCRIPT_NAME}.log"
readonly TMP_DIR=$(mktemp -d)
trap 'rm -rf "$TMP_DIR"' EXIT
log() { echo "[$(date '+%T')] $*" | tee -a "$LOG_FILE"; }
die() { log "FATAL: $*" >&2; exit 1; }
require_cmd() {
command -v "$1" &>/dev/null || die "需要命令: $1"
}
require_root() {
[[ $EUID -eq 0 ]] || die "需要root权限"
}
# 参数解析
while getopts "hv" opt; do
case $opt in
h) echo "Usage: $0 [options]"; exit 0 ;;
v) set -x ;;
*) exit 1 ;;
esac
done
main() {
require_cmd curl
log "开始执行"
# 业务逻辑
log "完成"
}
main "$@"
# 问题脚本
#!/bin/bash
name = $1
if [ $name = "" ]; then
echo "需要参数"
exit
fi
for f in $(ls *.txt); do
cat $f | grep error
echo "errors in $f"
done
# 修复后:
#!/bin/bash
set -euo pipefail
name="$1"
if [[ -z "$name" ]]; then
echo "需要参数" >&2; exit 1
fi
for f in *.txt; do
if grep -q error "$f"; then
echo "errors in $f"
fi
done✅ 掌握调试方法
✅ 学会shellcheck
✅ 理解常见错误
✅ 掌握错误处理
✅ 学会健壮脚本编写
🎉 阶段四(Shell脚本)完成!下一阶段:自动化
# 使用bats测试Shell脚本
# 安装
sudo apt install bats
# test_myapp.bats
#!/usr/bin/env bats
@test "帮助信息" {
run ./myapp.sh -h
[ "$status" -eq 0 ]
[[ "$output" =~ "Usage" ]]
}
@test "参数校验" {
run ./myapp.sh
[ "$status" -eq 1 ]
}
@test "正常执行" {
run ./myapp.sh test
[ "$status" -eq 0 ]
}
# 运行测试
bats test_myapp.bats
# .gitlab-ci.yml 示例
stages:
- test
- deploy
test:
stage: test
script:
- shellcheck *.sh
- bats tests/
deploy:
stage: deploy
script:
- ./deploy.sh
only:
- main
# deploy.sh
#!/bin/bash
set -euo pipefail
ansible-playbook -i inventory deploy.yml
#!/bin/bash
# 脚本: deploy.sh
# 描述: 部署应用到生产环境
# 用法: ./deploy.sh [-h] [-e ENV] APP_NAME
# 选项:
# -h 显示帮助
# -e ENV 部署环境(staging/production)
# 作者: ops-team
# 创建: 2024-01-15
# 更新: 2024-03-20 - 添加回滚功能
# 使用shdoc生成文档
# shdoc < deploy.sh > deploy.md
| 书名 | 作者 | 重点 |
|---|---|---|
| Linux命令行与Shell脚本编程大全 | Blum | Shell脚本全面覆盖 |
| UNIX/Linux系统管理技术手册 | Nemeth | 系统管理圣经 |
| Ansible: Up and Running | Lintner | Ansible实战 |
| 鸟哥的Linux私房菜 | 鸟哥 | 中文经典入门 |
| 认证 | 机构 | 级别 |
|---|---|---|
| LFCS | Linux Foundation | 基础系统管理 |
| LFCE | Linux Foundation | 高级系统工程师 |
| RHCSA | Red Hat | Red Hat管理员 |
| RHCE | Red Hat | Red Hat工程师 |
| CKA/CKAD | CNCF | Kubernetes管理员/开发者 |
# 文本处理速查
grep -E 'pattern' file # 扩展正则搜索
sed -i 's/old/new/g' file # 替换
awk '{print $1}' file # 提取列
sort | uniq -c | sort -rn # 频次统计
cut -d: -f1 file # 切列
tr 'A-Z' 'a-z' # 转小写
# 系统管理速查
systemctl status/start/stop/restart service
journalctl -u service -f # 跟踪日志
df -h / du -sh * # 磁盘使用
ps aux --sort=-%mem # 内存排序
ss -tlnp # 监听端口
iptables -L -n # 防火墙规则
crontab -e # 编辑定时任务
useradd -m -s /bin/bash user # 创建用户
# 网络诊断速查
ip addr / ip route # 网络配置
ping / traceroute # 连通测试
dig / nslookup # DNS查询
curl -I url # HTTP头检查
nc -zv host port # 端口测试
tcpdump -i eth0 port 80 # 抓包
$ bash -n -c 'echo hello'
(无输出 = 语法正确)
$ which shellcheck 2>/dev/null || echo "shellcheck: 未安装"
shellcheck未安装
#!/bin/bash
# script-monitor.sh
SCRIPTS=("/opt/backup.sh" "/opt/sync.sh")
for s in "${SCRIPTS[@]}"; do
[[ ! -f "$s" ]] && echo "Missing: $s" && continue
pid=$(pgrep -f "$s" | head -1)
[[ -n "$pid" ]] && echo "$s running PID=$pid" || echo "$s idle"
done
# 脚本超时检测
timeout_check() {
local pid="$1" max_sec="$2"
local sec=$(ps -o etimes= -p "$pid" 2>/dev/null | tr -d ' ')
[[ -n "$sec" ]] && [[ $sec -gt $max_sec ]] && echo "TIMEOUT: $pid ${sec}s"
}
在实际生产环境中,脚本质量保证体系是系统管理员必须深入掌握的核心能力。本节将探讨常见场景、最佳实践和避坑指南。
开发环境和生产环境的差异是脚本出错的常见原因。建议:
优化策略:
改善方法:
#!/bin/bash
# 脚本质量保证体系最佳实践模板
set -euo pipefail
# 1. 参数校验
[[ $# -lt 1 ]] && echo "Usage: $0 <arg>" >&2 && exit 1
# 2. 环境检查
require_cmd() {
command -v "$1" >/dev/null || { echo "需要: $1" >&2; exit 1; }
}
# 3. 清理机制
TMPDIR=$(mktemp -d)
trap 'rm -rf "$TMPDIR"' EXIT
# 4. 日志记录
log() { echo "[$(date +%T)] $*"; }
err() { log "ERROR: $*" >&2; exit 1; }
# 5. 主逻辑
main() {
log "开始处理: $1"
# 业务逻辑
log "处理完成"
}
main "$@"
# 文件操作
ls -la # 详细列表
find / -name "*.conf" 2>/dev/null # 查找文件
ln -s target link # 符号链接
du -sh * | sort -rh # 目录大小排序
# 文本处理
grep -rn "pattern" dir/ # 递归搜索
sed -i.bak 's/old/new/g' f # 备份替换
awk -F: '{print $1}' file # 提取列
sort file | uniq -c # 去重计数
# 系统管理
systemctl status/start/stop/restart service
journalctl -u service -f # 实时日志
ps aux --sort=-%mem # 内存排序
ss -tlnp # 监听端口
crontab -e # 定时任务
# 网络诊断
ip addr/route/show # 网络配置
ping/traceroute # 连通测试
dig/nslookup # DNS查询
curl -I url # HTTP检查
nc -zv host port # 端口测试
# 磁盘管理
df -h # 磁盘使用
fdisk -l # 分区信息
mkfs.ext4 /dev/sdb1 # 格式化
mount /dev/sdb1 /mnt # 挂载
blkid # UUID查看
本章内容是Linux系统管理课程的重要组成部分。掌握这些知识需要大量的实践操作,建议在虚拟机或云服务器上反复练习。记住:纸上得来终觉浅,绝知此事要躬行。
在实际工作中,遇到问题时: