问题与目标
重复输入命令容易漏步骤,也难以确认失败发生在哪里。Shell 脚本适合把环境检查、构建和启动等系统操作固化为可重复流程。
完成标准:能编写接收参数的 Bash 脚本,正确处理引号、条件、循环、函数和退出码,并在关键步骤失败时停止。
核心概念
脚本首行 #!/usr/bin/env bash 指定解释器。$0 是脚本名,$1 是第一个参数,$# 是参数数量,$? 是上一条命令的退出码。成功通常返回 0,失败返回非 0。
变量引用原则是默认写成 "$name",避免空格和通配符导致参数被重新拆分。单引号不展开变量,双引号会展开变量。
set -euo pipefail 分别让未处理的命令失败、未定义变量和管道中间失败暴露出来。它不能替代清晰的错误消息和显式分支。
几个容易混淆的特殊参数:
| 写法 | 含义 |
|---|---|
$# | 位置参数数量 |
"$@" | 保持每个参数原有边界,转发参数时优先使用 |
"$*" | 把所有参数合成一个字符串 |
$? | 上一条命令的退出状态 |
$(command) | 把命令输出作为值 |
整数计算使用 $((...)),读取输入使用 read -r。分支不只可以用 if,固定选项较多时 case 更清楚:
bash
mode=${1:-check}
case "$mode" in
check) echo "检查环境" ;;
run) echo "启动任务" ;;
*) echo "未知模式: $mode" >&2; exit 2 ;;
esac
for item in config data logs; do
printf '目录: %s\n' "$item"
done
可运行实现
保存为 check_files.sh:
bash
#!/usr/bin/env bash
set -euo pipefail
usage() {
echo "用法: $0 <目录> [扩展名]" >&2
}
if (( $# < 1 || $# > 2 )); then
usage
exit 2
fi
target_dir=$1
extension=${2:-py}
if [[ ! -d "$target_dir" ]]; then
echo "目录不存在: $target_dir" >&2
exit 1
fi
count=0
while IFS= read -r -d '' file; do
printf '%s\n' "$file"
((count += 1))
done < <(find "$target_dir" -type f -name "*.${extension}" -print0)
echo "共找到 ${count} 个 .${extension} 文件"
运行:
bash
chmod +x check_files.sh
./check_files.sh . md
echo "$?"
输入是目录和可选扩展名,输出是安全处理过的文件名和数量。-print0 配合 read -d '',可以处理文件名中的空格和换行。
脚本创建临时资源时,应让正常退出和异常退出都执行清理:
bash
work_dir=$(mktemp -d)
cleanup() {
rm -rf -- "$work_dir"
}
trap cleanup EXIT
这里的删除目标来自刚创建的、已知的临时目录。不要把未校验变量交给递归删除命令。
常见问题与排查
bad interpreter:检查首行解释器路径和 Windows 换行符,可用file 脚本名判断。unbound variable:启用-u后,给可选值使用${value:-默认值}。- 参数中空格被拆开:转发参数使用
command "$@",不要写成未加引号的$@。 set -e没有按预期退出:条件判断、逻辑连接和命令替换中的行为存在例外,关键步骤仍应使用if ! command; then ...; fi给出明确错误。[ ... ]中空格缺失:Bash 条件推荐使用[[ ... ]],两侧仍需正确分隔。((count++))在set -e下第一次可能返回失败状态,因此示例使用((count += 1))。- 调试时可临时执行
bash -x check_files.sh ...,不要在包含密钥的环境中随意输出所有变量。
小结
可靠脚本的核心不是命令多,而是参数明确、路径加引号、失败可见、退出码可信。脚本能否被别人重复执行,是工程化的第一道门槛。
#Python
#大模型
#AI
License: CC BY-NC 4.0
Updated 2 hours ago
Was this article helpful? Give it a like.
0 comments


