shell判断文件夹是断文否存在
写shell脚本最常干的事之一,就是否存判断一个文件夹在不在。别小看这个动作,断文搞错了,否存后面的断文rm -rf能把你整台服务器清空。我见过不止一次,否存脚本里没判断目录存在就执行删除,断文变量一空,否存路径直接变成根目录,断文那场面,否存运维当场辞职。断文 if [ -d "/path/to/dir" ]; then echo "目录存在" else echo "目录不存在" fi
这是否存最朴素的写法,`-d`就是断文专门测目录的。文件用`-f`,否存软链接得加`-L`,断文别混。有一次我排查线上问题,脚本明明判断了目录存在才执行备份,结果还是报错。后来发现那个目录是个软链接,指向的源目录被挪走了,`-d`对悬空的软链接返回假。你以为它在,其实它不在了,这种坑,不踩一次不长记性。 判断完不存在,通常得创建。很多人直接`mkdir -p`,这命令自带“不存在就建,存在也不报错”的属性。所以有些场景根本不用先判断,一条`mkdir -p`搞定。但如果你要基于目录是否存在来决定不同逻辑分支,那还是得老老实实写if。 if [ ! -d "$BACKUP_DIR" ]; then mkdir -p "$BACKUP_DIR" echo "备份目录已创建" fi
注意那个`!`,取反。意思是没有这个目录才创建。但这里有个细节——变量要加引号。你写`[ -d $BACKUP_DIR ]`,万一变量是空的,展开后变成`[ -d ]`,这玩意儿永远返回真,你的判断就废了。加引号,养成习惯,跟系安全带一样。 还有一种情况,目录存在但没写权限。你光判断存在没用,得测能不能写。`[ -w "$DIR" ]`干这个。我写部署脚本时经常连着判断:先看目录在不在,不在就创建,创建完再测写权限,不行就报错退出。三层检查,看着啰嗦,但线上跑起来稳。 if [ -d "$APP_DIR" ] && [ -w "$APP_DIR" ]; then cp "$SOURCE" "$APP_DIR/" else echo "目录不可写或不存在" >&2 exit 1 fi
`&&`把两个条件串起来,都得满足才执行。这种写法比嵌套if清爽多了。你要是还嫌长,可以用`[[ ]]`替代`[ ]`,支持正则和通配符,但注意`[[ ]]`是bash的扩展语法,`sh`跑不了。写脚本开头最好声明`#!/bin/bash`,不然默认sh跑,遇到`[[ ]]`直接报错,你查半天不知道哪错了。 判断文件夹存在这事儿,看着简单,实际写多了你会发现,真正的复杂度不在判断本身,而在判断之后你要干什么。是创建?是跳过?还是报错退出?这决定了你要不要加`set -e`,要不要捕获错误。我的习惯是,脚本开头加`set -euo pipefail`,这样任何命令失败都会立即退出,变量没定义也报错。配合目录判断,基本能把大部分低级失误堵死。 最后说个偏方。有时候你只是想确认某个目录结构是否完整,比如部署目录下必须有`bin`、`conf`、`logs`三个子目录,少一个就算失败。这种就别写三个if了,循环遍历一个列表,简洁得多: for sub in bin conf logs; do if [ ! -d "$BASE_DIR/$sub" ]; then echo "缺少目录: $sub" >&2 exit 1 fi done
写shell跟写别的语言不一样,它没有那么多花哨的语法糖,但每一条命令背后都是真实的系统调用。判断目录是否存在,本质上就是一次`stat`系统调用。理解这一点,你就明白为什么判断要快,为什么目录挂载在NFS上时,这个操作会卡住——网络文件系统有延迟,stat要等远程响应。 总之,`-d`、引号、`&&`、`set -e`,这几个词记住,shell里判断文件夹存在这事儿,基本就玩明白了。剩下的,都是你踩过的坑替你交的学费。