使用awk拆分大mbox文件时,sprintf是否存在缓冲区溢出?或其他问题?
问题原因分析
你的报错核心原因不是sprintf缓冲区溢出,而是脚本中关闭文件的文件名与实际输出的文件名不匹配,导致awk同时打开的文件数达到系统上限(多数系统默认是256左右),无法再打开新文件。
具体来说:
- 你修改了输出文件名的格式为
sprintf("%04d",chunk),生成带前导零的文件名(比如chunk_0253.txt) - 但脚本中
close()语句使用的是"chunk_" chunk ".txt",生成不带前导零的文件名(比如chunk_253.txt) - 这两个是完全不同的文件,所以旧的输出文件根本没有被关闭。随着处理的邮件增多,awk打开的文件数持续积累,直到达到系统允许的同时打开文件上限(通常为256,减去awk自身预打开的几个文件后,剩余可用数约253),就会触发
can't open file错误。
而使用%03d时能正常运行,是因为当chunk数值小于1000时,sprintf("%03d",chunk)生成的文件名(比如chunk_253.txt)和"chunk_" chunk ".txt"的结果一致,close()能正确关闭旧文件,不会积累打开的文件数。
修复方案
修改脚本中的close()语句,使其使用和输出一致的文件名格式:
BEGIN{chunk=0;filesize=0;} /^From /{ if(filesize>=0){#file size per chunk in byte close("chunk_" sprintf("%04d",chunk) ".txt"); filesize=0; chunk++; } } {filesize+=length()} {print > ("chunk_" sprintf("%04d",chunk) ".txt");}
这样每次切换chunk时,都会正确关闭之前的带前导零的文件,避免打开文件数超限。
额外优化提示
- 如果你确实需要每个邮件单独一个文件(因为你设置了
filesize>=0,每次遇到^From就切换文件),可以简化逻辑,同时确保每次只打开一个文件:
BEGIN{chunk=0;} /^From /{ if(chunk>0){ close("chunk_" sprintf("%04d",chunk) ".txt"); } chunk++; } {print > ("chunk_" sprintf("%04d",chunk) ".txt");}
- 可以通过
ulimit -n命令查看当前系统允许的最大同时打开文件数,若需要临时调整,可执行ulimit -n 1024(仅对当前终端会话有效)。
内容的提问来源于stack exchange,提问作者ecjb
相关产品推荐
相关产品推荐

