如何在Bash中仅打印父路径并排除所有子目录路径
需求:提取目录的最顶层父路径(排除子目录路径)
背景
之前解决过反向问题,现在要实现相反逻辑:从find输出的文件路径里,只保留最顶层的父目录,把所有子目录路径都排除掉。
原反向逻辑命令
原来用来排除父路径、保留子路径的命令是:
cat file | sort | tac | awk '{sub("/[^/]+$","")} index(prev,$0"/") != 1{print} {prev=$0}'
其中file是这条find命令的输出结果:
find src/services/ -type f -name 'BUILD.bazel'
示例输入(file内容)
src/services/sam-agent/BUILD.bazel src/services/sam-agent/auth/BUILD.bazel src/services/sam-agent/certs/BUILD.bazel src/services/sam-agent/server/BUILD.bazel src/services/jam-controller/BUILD.bazel src/services/jam-controller/api/BUILD.bazel src/services/jam-controller/client/BUILD.bazel src/services/wam-controller/api/BUILD.bazel src/services/wam-controller/api/client/BUILD.bazel src/services/wam-controller/api/server/BUILD.bazel
期望输出
只保留最顶层的父路径,其他子目录全部排除:
src/services/sam-agent src/services/jam-controller src/services/wam-controller
也就是只打印下面标记的路径,剩下的子目录都不输出:
src/services/sam-agent #< 需打印
src/services/sam-agent/auth
src/services/sam-agent/certs
src/services/sam-agent/server
src/services/jam-controller #< 需打印
src/services/jam-controller/api
src/services/jam-controller/client
src/services/wam-controller/api #< 需打印
src/services/wam-controller/api/client
src/services/wam-controller/api/server
尝试的错误命令及问题
试了下面的命令,但还是会输出子目录(比如src/services/rams/store/postgres),没能只留下最顶层路径:
cat /tmp/t.log | sort | tac | awk '{if ($0 in seen) print; else {sub("/[^/]+$", ""); seen[$0]}}'
/tmp/t.log的内容示例:
src/services/rams src/services/rams/integration src/services/rams/keys src/services/rams/migrations src/services/rams/mocks src/services/rams/server src/services/rams/smoke src/services/rams/store src/services/rams/store/postgres src/services/rams/store/postgres/mocks src/services/rams/tools/keyrotate src/services/rams/tools/secretmanager src/services/rams/vault
错误输出结果:
src/services/rams/store/postgres src/services/rams
解决方案
方法1:处理已有文件的高效实现
先对路径排序,再用awk筛选出顶层目录:
sort file | awk '{ dir = $0 sub("/[^/]+$", "", dir) # 去掉末尾的文件名,得到目录路径 if (!prev || index(dir, prev "/") != 1) { if (prev) print prev prev = dir } } END { print prev }'
方法2:直接处理find输出(无需中间文件)
如果不需要保存file中间文件,直接把find和处理逻辑连起来:
find src/services/ -type f -name 'BUILD.bazel' | sort | awk '{ dir = $0 sub("/[^/]+$", "", dir) if (!prev || index(dir, prev "/") != 1) { if (prev) print prev prev = dir } } END { print prev }'
方法3:简化版sed+awk实现
先提取所有目录路径,排序后只保留非子目录的顶层路径:
find src/services/ -type f -name 'BUILD.bazel' | sed 's/\/[^/]*$//' | sort | awk ' !prev || !index($0, prev "/") { print $0; prev = $0 } '
方案说明
- 先对路径按字典序排序,这样子目录会自动跟在对应的父目录后面
- 对每一行路径,去掉最后的文件名,得到纯目录路径
- 检查当前目录是否是上一个目录的子目录:如果不是,说明上一个目录是顶层路径,输出它;如果是,则跳过继续往下
- 最后在命令结束时输出最后一个顶层目录
内容的提问来源于stack exchange,提问作者AhmFM
相关产品推荐
相关产品推荐

