写Shell脚本看起来简单,几行命令拼一拼就能跑。可真要写稳了,不翻车,还真得踩过几个坑才明白。尤其是新手,一不小心就把服务器搞卡了,或者数据删错了,连哭都来不及。
变量没加引号,空格惹的祸
最常见的问题就是变量不加引号。比如你有个路径带空格:
filepath=/home/user/my documents/file.txt
ls $filepath
这一跑,系统就当成三个参数处理:/home/user/my、documents/file.txt,直接报“找不到文件”。正确的写法是:
ls "$filepath"
加上双引号,整个变量当一个整体传进去,才不会被拆开。
忘记判断命令是否执行成功
很多人写脚本只管往下冲,不管上一步成没成。比如备份数据库:
mysqldump -u root db_name > backup.sql
cp backup.sql /mnt/backup/
万一导出失败,backup.sql 是空的,你还往备份目录拷,那就把上次的好数据给覆盖了。应该加个判断:
if mysqldump -u root db_name > backup.sql; then
cp backup.sql /mnt/backup/
else
echo "数据库备份失败,停止操作"
exit 1
fi
for循环读文件用cat,结果出问题
想逐行处理一个文件,很多人这么写:
for line in $(cat file.txt); do
echo "处理: $line"
done
看着没问题,实际 $(cat file.txt) 会把所有内容展开成一堆词,按空白符分割,不是按行。如果某行有空格,就被拆成两行处理了。正确做法是用 while read:
while IFS= read -r line; do
echo "处理: $line"
done < file.txt
临时文件名字太随意,容易冲突
脚本里生成临时文件,图省事写个/tmp/data.tmp,多个实例一跑,互相覆盖。更糟的是,别人也可能用这个名字,搞出安全问题。别手动生成,用 mktemp:
tmpfile=$(mktemp)
echo "临时数据" > $tmpfile
# 用完记得删
rm -f $tmpfile
忽略信号中断,脚本中途退出留烂摊子
脚本正在改配置、搬文件,这时候你 Ctrl+C 一按,进程停了,但临时文件没删,锁没释放,下次再跑就冲突。可以用 trap 捕获中断:
tmpfile=$(mktemp)
trap 'rm -f $tmpfile; echo "清理完毕,退出"; exit' INT TERM
# 正常逻辑
echo "处理中..."
sleep 10
rm -f $tmpfile
这样无论怎么退出,都会先清理临时文件。
在路径里写绝对路径,换台机器就废
有些脚本开头就写死:
cd /home/developer/project/scripts
换个用户,路径不对,直接报错。如果脚本就在项目里,可以用相对路径定位自己:
SCRIPT_DIR=$(dirname "$(readlink -f "$0")")
cd $SCRIPT_DIR
这样不管谁在哪个目录下执行,都能正确切换到脚本所在位置。
写Shell脚本不像写Python或Java那样有编译检查,很多问题运行时才暴露。但只要避开这些常见陷阱,脚本就能稳得多。别嫌麻烦,该加引号加引号,该判错就判错,省得半夜被报警电话叫起来修bug。