Appearance
脚本工程、调试与 Bash 扩展
- 状态:已验证
- 来源:对话整理
- 更新时间:2026-03-12
关键结论
- 脚本工程部分应优先覆盖路径处理、参数解析、调试、
trap、后台进程和常见模板。 - Bash 扩展如模式匹配、正则、进程替换和
shopt很实用,但必须明确 Bash 依赖。
详细分析
- 大多数可维护性问题不是语法本身,而是脚本目录定位、参数处理、错误定位和清理逻辑缺失。
set -x、trap、getopts和进程控制是排查复杂脚本问题的高频工具。
可执行步骤
- 在脚本开头先确定脚本目录、严格模式和清理机制。
- 使用
getopts处理短参数。 - 遇到问题时先用
set -x或bash -x观察执行过程。 - 使用 Bash 扩展前,确保脚本明确声明 Bash 依赖。
命令 / 配置 / 代码
脚本工程
高频写法清单:
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"trap '...' EXITwhile getopts ":f:o:h" opt; do ... doneset -xbash -x script.shcmd &wait "$pid"
路径与脚本目录
"$(dirname "$0")"获取脚本路径的目录部分。"${BASH_SOURCE[0]}"当前 Bash 脚本文件路径。"$(cd ... && pwd)"获取绝对路径。
bash
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"Bash 扩展
高频写法清单:
[[ "$x" == *.sh ]][[ "$x" =~ regex ]]<(cmd)>(cmd)shopt -s nullglobshopt -s globstar
通配符与模式匹配
*匹配任意长度字符。?匹配单个字符。[abc]匹配字符集合中的任一字符。[a-z]匹配字符范围。[[ "$x" == *.md ]]在 Bash 条件中进行模式匹配。
bash
for f in *.md; do
echo "$f"
done
if [[ "$file" == *.sh ]]; then
echo "shell script"
fi正则匹配
[[ "$x" =~ regex ]]使用 Bash 正则进行匹配。${BASH_REMATCH[0]}完整匹配内容。${BASH_REMATCH[1]}第一个捕获组。
bash
text="user:1001"
if [[ "$text" =~ ^([a-z]+):([0-9]+)$ ]]; then
echo "${BASH_REMATCH[1]}"
echo "${BASH_REMATCH[2]}"
fi模板
高频写法清单:
- 参数数量校验
- 文件存在性校验
- 临时文件清理
trap ... EXIT
常见写法模板
bash
if [ $# -lt 1 ]; then
echo "usage: $0 <file>"
exit 1
fibash
if [ ! -f "$1" ]; then
echo "file not found: $1"
exit 1
fibash
trap 'echo "cleanup"; rm -f "$tmp_file"' EXITtrap 与信号处理
trap 'cmd' EXIT脚本退出时执行清理命令。trap 'cmd' INT捕获Ctrl+C。trap - EXIT清除已设置的 trap。
bash
tmp_file="$(mktemp)"
trap 'rm -f "$tmp_file"' EXIT进程与后台执行
cmd &后台执行命令。wait等待所有后台任务结束。wait "$pid"等待指定进程。jobs查看当前 shell 后台任务。kill "$pid"向指定进程发送信号。
bash
sleep 10 &
pid="$!"
echo "$pid"
wait "$pid"常用内建命令
echo输出文本。printf格式化输出,通常比echo更稳定。read读取输入。test条件测试,等价于[ ... ]。shift左移位置参数。export导出环境变量。declare声明变量属性。
bash
printf '%s\n' "$name"
export APP_ENV=prod
shift参数解析与 getopts
while getopts ":f:o:h" opt; do ... done解析短选项参数。OPTARG当前选项携带的参数值。OPTIND下一个待处理参数的索引。shift $((OPTIND - 1))移除已经解析过的选项参数。
bash
file=""
output=""
while getopts ":f:o:h" opt; do
case "$opt" in
f) file="$OPTARG" ;;
o) output="$OPTARG" ;;
h)
echo "usage: $0 -f <file> -o <output>"
exit 0
;;
:)
echo "option requires an argument: -$OPTARG"
exit 1
;;
\?)
echo "invalid option: -$OPTARG"
exit 1
;;
esac
done
shift $((OPTIND - 1))调试与排错
set -x打开执行跟踪,打印每条执行命令。set +x关闭执行跟踪。set -v输出 shell 读取到的脚本原文。bash -x script.sh以调试模式执行脚本。trap '...' ERR命令出错时执行错误处理逻辑。PS4='+ ${BASH_SOURCE}:${LINENO}: '自定义set -x的调试输出前缀。
bash
set -x
echo "debug"
set +xbash
PS4='+ ${BASH_SOURCE}:${LINENO}: '
set -xbash
trap 'echo "error on line $LINENO"' ERR
set -e进程替换
<(cmd)将命令输出伪装成一个可读取的文件路径。>(cmd)将输出重定向到某个命令的输入。
bash
diff <(sort a.txt) <(sort b.txt)bash
echo "hello" > >(tee output.txt)shell 选项与 shopt
set -o查看或设置 shell 选项。set +o以可重放形式输出当前选项。shopt查看或设置 Bash 扩展选项。shopt -s nullglob开启某个 Bash 扩展选项。shopt -u nullglob关闭某个 Bash 扩展选项。
bash
set -o
set +o
shoptbash
shopt -s nullglob
files=(*.md)
shopt -u nullglob风险与注意事项
getopts主要处理短选项,复杂长选项解析通常需要额外逻辑。- 打开
set -x时,敏感信息可能被直接打印到日志或终端。 <(cmd)、>(cmd)、[[ ... ]]、数组和shopt都依赖 Bash,不应误标为通用sh语法。