高效处理脚本参数和错误日志需结构清晰、防御性强、可追溯:参数处理须加引号、用getopts或while-case解析;日志要分层、带时间戳和上下文,错误重定向stderr;错误处理启用set -euo pipefail,配合trap捕获行号与退出码;日志归档需唯一路径、自动重定向、敏感信息脱敏及定期rotate。

高效处理脚本参数和错误日志,核心在于结构清晰、防御性强、可追溯。参数要能灵活接收又不误解析,日志要能准确定位问题而不干扰正常流程。
参数处理:避免空格陷阱与位置混乱
直接用 $1、$2 容易出错,尤其当参数含空格或特殊字符时。必须加引号并配合合理解析机制。
- 所有变量引用统一加双引号,如 "$1"、"$FILE_PATH",防止路径或文件名含空格导致命令拆分
- 超过 9 个参数时,用 "${10}" 而非 $10(后者会被解释为 $1 后跟字符 0)
- 推荐用 getopts 处理带选项的参数,例如 -f /path -v --debug,它自动校验格式、支持短选项组合,并跳过非法参数
- 若需键值对式传参(如 --host db.example.com --port 5432),可用 while-case 循环解析,每次 shift 2 消耗一对参数,比纯位置参数更健壮、更易读
错误日志:区分层级,保留上下文
日志不是越多越好,而是要让运维或开发者一眼看出“哪里错了、为什么错、怎么复现”。
- 普通信息走 stdout,错误信息强制重定向到 stderr:echo "Failed to connect" >&2,避免被管道或重定向意外吞掉
- 每条日志带上时间戳和来源,例如:log() { echo "[$(date '+%H:%M:%S')] [ERROR] $*" >&2; },便于多进程或多轮执行时对齐时间线
- 关键操作前记录意图,操作后记录结果。比如执行 rm "$target" 前先 log "Removing: $target",失败后再捕获退出码补充详情
- 临时文件、调试输出不要混入主日志;可单独设 DEBUG_LOG="/tmp/script_debug_$$",用 set -x 2>>"$DEBUG_LOG" 保存完整执行流
错误处理:不让脚本“带病运行”
默认情况下 Shell 遇错不报、继续往下跑,这是生产环境最危险的行为。
- 开头就加 set -euo pipefail:-e 让任意命令失败即终止;-u 拦截未定义变量使用;-o pipefail 确保 cat file | grep x | wc -l 中 cat 失败时整个管道失败
- 对关键外部命令做显式判断,例如 if ! command -v jq >/dev/null; then log "jq not found"; exit 1; fi,比依赖 set -e 更主动
- 用 trap 'log "Error at line $LINENO"; exit $?' ERR 捕获任意位置的错误,并附上出错行号和真实退出码
- 清理动作写在 trap EXIT 里,比如 trap 'rm -f "$TEMP_FILE"' EXIT,确保无论成功失败都释放资源
日志归档与可维护性
日志长期留存需兼顾可读性与空间控制,不能只写不管。
- 脚本启动时生成唯一日志路径,例如 LOG_FILE="/var/log/${SCRIPT_NAME%.sh}_$(date +%Y%m%d).log",避免多实例覆盖
- 用 exec >>"$LOG_FILE" 2>&1 将后续所有输出(含 stderr)自动追加进日志,省去每行手动重定向
- 敏感信息如密码、token 必须过滤,可在写入前用 sed 或参数替换脱敏:echo "$cmd" | sed 's/--password=[^ ]*/--password=*** /g'
- 定期 rotate 日志,或调用 logrotate 配置,避免单个文件无限增长


















