Shell脚本应避免硬编码:用$BASH_SOURCE[0]获取脚本路径,参数用${1:-default}设默认值,敏感配置外置并设600权限;处理空格文件名需find -print0配合read -d '';通过[[ "${BASH_SOURCE[0]}" == "${0}" ]]区分直接执行与source调用;cron中须显式声明PATH、用绝对路径、指定bash解释器。

Shell 脚本里怎么避免硬编码路径和参数
硬编码是自动化脚本最常崩的点——换台机器、换个用户、甚至换个项目目录,./deploy.sh 就报 No such file or directory。根本不是语法错,是路径失效。
解决思路就一条:把可变部分抽成变量,且优先从环境或调用时传入,而不是写死。
-
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)—— 获取脚本自身所在绝对路径,比pwd可靠得多 - 用
${1:-"/opt/app"}表示“第一个参数,没传就用默认值”,避免空值导致命令失败 - 敏感配置(如密码、token)绝不写进脚本,改用
source ./env.conf或读取$HOME/.mytoolrc,且确保该文件权限为600
批量处理文件时为什么 find + while 读不出带空格的文件名
这是 Shell 新手掉进最多次的坑:find . -name "*.log" | while read f; do echo $f; done 遇到 error log 2024.txt 就只输出 error。
本质是 read 默认按空白符分割,而 find 的输出没有做安全分隔。正确做法是让 find 控制分隔方式,再配对处理:
find . -name "*.log" -print0 | while IFS= read -r -d '' file; do echo "Processing: $file" # 这里可以安全使用 $file,含空格、换行都 OK done
关键点:-print0 让 find 用 \0 分隔,read -d '' 对应用 \0 当行尾,IFS= 和 -r 防止反斜杠被误解析。
如何让一个脚本既可单独运行,又能被 source 复用函数
想把常用逻辑(比如日志打印、服务状态检查)封装成函数库,又不想每次都要 source utils.sh 才能跑主脚本——那就得判断执行上下文。
核心依据是:$0 指向当前执行文件名,而 source 时 $0 不变,仍是外层 shell 的名字(如 -bash)。所以:
- 用
[[ "${BASH_SOURCE[0]}" == "${0}" ]]判断是否直接执行(即./script.sh) - 如果是,就调用主逻辑;如果不是,就只定义函数,不执行
#!/bin/bashlog_info() { echo "[$(date +'%H:%M:%S')] INFO: $*" }
只有直接运行本脚本时才执行下面这段
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then log_info "Starting deployment..."
主流程放这里
fi
定时任务(cron)里脚本不生效,但手动 run 就 OK
cron 环境极简:PATH 基本只有 /usr/bin:/bin,没加载 ~/.bashrc,连 which jq 都可能返回空。这不是脚本问题,是环境缺失。
两个必须动作:
- 在 crontab 里显式声明 PATH,比如
PATH=/usr/local/bin:/usr/bin:/bin - 脚本开头加
set -eux,并重定向所有输出:*/5 * * * * /path/to/backup.sh >> /var/log/backup.log 2>&1,否则错误全丢进黑洞 - 绝对路径!
python3改成/usr/bin/python3,mysqldump改成/usr/bin/mysqldump,用which查准位置
还有个隐藏雷区:cron 默认 SHELL 是 /bin/sh,不支持 [[ 或数组语法。要么改成 #!/bin/bash 并显式调用 bash /path/to/script.sh,要么全用 POSIX 兼容写法。










