Bash技巧:如何每次提取3个文件的父目录名称
问题与解决方案
需求说明
现有一批文件路径,需要完成以下操作:
- 按字母顺序排序文件路径
- 每3个文件路径为一组
- 提取每组中每个文件的父文件夹名称
- 命令需支持含空格的路径
原始路径示例:
/foo/bar/bazz/JMA01023D_E07/JMA01023D_E07_EKDL230054768-1A_22HFKNLT3_L4_1.fq.gz /foo/bar/bazz/JMA01023D_E08/JMA01023D_E08_EKDL230054768-1A_22HFKNLT3_L4_1.fq.gz /foo/bar/bazz/JMA01023D_E09/JMA01023D_E09_EKDL230054768-1A_22HFKNLT3_L4_1.fq.gz /foo/bar/bazz/JMA01022D_E06/JMA01022D_E06_EKDL230054767-1A_22HF2WLT3_L7_1.fq.gz /foo/bar/bazz/JMA01001D_A01/JMA01001D_A01_EKDL230054750-1A_222T7MLT4_L1_1.fq.gz /foo/bar/bazz/JMA01001D_A02/JMA01001D_A02_EKDL230054750-1A_222T7MLT4_L1_1.fq.gz
期望输出:
JMA01001D_A01 JMA01001D_A02 JMA01022D_E06 JMA01023D_E07 JMA01023D_E08 JMA01023D_E09
可行解决方案
方案1:纯Awk处理(推荐,简洁且支持空格)
find "$@" -iname '*_1.fq.gz' -print0 | sort -z | awk -v RS='\0' '{ # 移除文件名,得到父目录完整路径 sub(/\/[^\/]+$/, "", $0) # 移除父目录前的所有路径,仅保留目录名 sub(/.*\//, "", $0) # 将目录名存入数组 arr[NR] = $0 } END { # 按每3个一组输出 for (i=1; i<=NR; i++) { printf "%s%s", arr[i], (i%3==0 ? "\n" : " ") } # 处理最后一组不足3个的情况,补换行 if (NR%3 != 0) print "" }'
方案2:结合dirname、basename与xargs
find "$@" -iname '*_1.fq.gz' -print0 | sort -z | \ xargs -0 -n1 sh -c 'basename "$(dirname "$1")"' _ | \ xargs -0 -n3 awk '{printf "%s ", $0} END{print ""}'
- 步骤解析:
dirname "$1"获取文件的父目录完整路径basename提取父目录的名称xargs -n3每3个目录名一组,用Awk格式化为空格分隔的行
方案3:Awk分割路径数组实现
find "$@" -iname '*_1.fq.gz' -print0 | sort -z | awk -v RS='\0' -v ORS=' ' '{ # 按/分割路径,取倒数第二个元素(父文件夹名) split($0, path_parts, "/") dir_name = path_parts[length(path_parts)-1] print dir_name # 每3个输出后换行 if (NR % 3 == 0) { print "\n" ORS = " " } }' | sed -z '$ s/ $/\n/'
- 最后用
sed移除末尾多余的空格并补换行,保证输出格式整洁
内容的提问来源于stack exchange,提问作者TravelingFox
相关产品推荐
相关产品推荐

