Bash中grep/tail读取持续更新文件挂起的解决方法
问题根因
读取挂起是两个场景特性共同导致的:
- tqdm进度条依靠
\r回车符刷新同行内容,不会输出换行符\n,.out文件末尾会存在很长一段无换行的内容,grep、tail默认按行做读取统计时,会持续等待有效换行符判定行边界,卡住无法退出 - 加了
python -u参数后文件处于实时追加写入状态,常规读取逻辑会自动跟踪文件最新写入位置,不会固定在命令启动瞬间的文件状态做静态读取。
解决方案
核心思路是给文件打瞬时快照:读取前先获取当前时刻的文件字节长度,仅读取对应长度的内容,读取过程中新写入的内容全部忽略,读完立刻退出,从根源上规避挂起问题。
以下命令可以直接实现需求,不会出现卡顿:
find . -name "*.out" -print0 | while IFS= read -r -d '' file; do # 获取命令执行瞬间的文件字节大小 fsize=$(stat -c "%s" "$file") # 仅读取该长度的快照内容,取最后两行判断模块缺失错误 if head -c "$fsize" "$file" | tail -n 2 | grep -q "No module"; then echo "$file" fi done
命令细节说明
- 用
find -print0配合read -d ''遍历文件,是为了兼容文件名带空格、特殊字符的情况,比直接写for file in $(find)鲁棒性更强,不会把带空格的文件名拆成多个无效路径 stat -c "%s"取到的是执行这条命令瞬间的文件字节数,后续文件新写入的内容不会影响这个数值,相当于给文件大小拍了固定快照head -c $fsize按字节读取,读够指定长度就立刻退出,不会等待文件新写入内容,完全规避持续写入文件导致的读取挂起问题- 快照内容传给
tail -n 2取最后两行(包含你需要的倒数第二行),用grep -q静默匹配No module关键字,匹配到就输出对应失败任务的日志路径。
大文件提速优化
如果单份实验日志体积很大,全量读快照速度慢,可以改成只读文件末尾固定长度的片段——模块缺失报错一定会在程序退出前的最后位置输出,读末尾4KB足够覆盖错误信息和最后一段tqdm进度条内容,大文件场景下速度提升明显:
find . -name "*.out" -print0 | while IFS= read -r -d '' file; do # 按字节读取末尾4KB内容,无需等待换行符,不会卡顿 tail -c 4096 "$file" | tail -n 2 | grep -q "No module" && echo "$file" done
内容的提问来源于stack exchange,提问作者Kraigolas
相关产品推荐
相关产品推荐

