如何高效合并目录下所有文件的第4列?
高效合并多文件指定列的解决方案
问题背景
需要合并目录下400个各含50万行的制表符分隔文件的第4列,要求列依次向右追加,for循环处理速度过慢。
示例输入(三个文件)
# file1内容 col1 col2 col3 col4 col5 a 0 0 -1 0.001 b 1 0 2 0.004 c 2 0 3 0 # file2内容 col1 col2 col3 col4 col5 c 2 0 -9 0.004 s 1 0 5 0.002 d 3 0 3 0.4 # file3内容 col1 col2 col3 col4 col5 r 2 1 0 0.4 j 1 1 1 0.2 r 3 1 2 0.1
期望输出
file1 file2 file3 -1 -9 0 2 5 1 3 3 2
你遇到的问题
你尝试先将文件转为CSV格式:
for file in $(ls); do awk '{$1=$1}1' OFS=',' ${file} > ${file}.csv; done
再执行合并命令:
eval paste -d, $(printf "<(cut -d, -f4 %s) " *.csv)
但触发错误:paste: /dev/fd/19: Too many open files——这是因为paste通过进程打开了太多文件描述符,超过了系统限制。
解决方案:用awk直接处理原文件
不需要转格式,用awk一次性处理所有文件,既高效又能避开文件描述符限制:
awk -F'\t' ' BEGIN { # 收集所有输入文件名 for (i=1; i<ARGC; i++) files[i] = ARGV[i] ARGC = 1 # 阻止awk默认遍历文件,后续手动处理 } FNR == 1 { next } # 跳过每个文件的表头行 { # 按行号和文件索引存储第4列内容 arr[FNR][ARGIND] = $4 if (FNR > max_row) max_row = FNR } END { # 输出表头(所有文件名) for (i=1; i<=length(files); i++) { printf "%s%s", files[i], (i==length(files)?"\n":"\t") } # 逐行输出各文件的第4列 for (row=2; row<=max_row; row++) { for (i=1; i<=length(files); i++) { printf "%s%s", arr[row][i], (i==length(files)?"\n":"\t") } } } ' *.tsv # 替换为你的实际文件后缀,比如*.txt或直接用*匹配所有目标文件
方案优势
- 无需格式转换:直接处理原制表符分隔文件,节省磁盘空间和转换时间
- 高效低耗:awk仅遍历所有文件一次,按行存储数据,内存占用可控
- 避免系统限制:不会像
paste那样打开大量进程导致文件描述符耗尽 - 兼容特殊文件名:即使文件名含空格或特殊字符也能正常处理(比你之前用
ls的循环更可靠)
内容的提问来源于stack exchange,提问作者Caterina
相关产品推荐
相关产品推荐

