使用pdfgrep排序文件时bash脚本卡住及运行缓慢问题咨询
问题修复与性能优化方案
输出卡顿、按Ctrl+C才批量打印日志的根因
- 管道块缓冲:Linux下程序输出走管道时默认使用4KB大小的块缓冲,而非面向终端的行缓冲。原脚本中
pdfgrep的输出通过管道传给xargs,匹配到的文件名会攒到缓冲满才会输出,无法实时打印进度。 - Shell通配符阻塞:原脚本写的
$HOME/PDFs/*会先由Shell展开目录下所有文件名,若目录下文件数量过万,这个展开过程会持续数秒到数分钟,期间无任何输出,表现为进程卡住。 - 执行逻辑无中间输出:原脚本用
xargs -I{}会等待pdfgrep完成全目录扫描、拿到所有匹配结果后才会执行复制操作,整个扫描阶段没有进度反馈。
执行耗时过长的根因
- 重复IO开销:原逻辑每匹配一个关键词,就要完整遍历、解析一遍所有PDF文件,关键词数量和耗时线性增长,是性能差的核心原因。
- 进程冗余开销:
xargs -I{} cp {}语法每匹配到一个文件就启动一次cp进程,文件量大时进程创建、销毁的开销占比极高。 - 无效扫描:未指定只扫描PDF后缀文件,目录下的非PDF文件也会被
pdfgrep尝试解析,浪费算力。 - 重复递归:传入通配符展开的文件列表同时加
-r递归参数,会导致子目录被重复遍历。
修复后优化脚本
#!/bin/bash set -euo pipefail # 关键词列表 keywords=( "Keyword1" "Keyword2" "Keyword3" ); # -p 参数自动创建不存在的目录,目录已存在时不报错 mkdir -p "$HOME/Sorted"; echo "匹配任务启动,共加载 ${#keywords[@]} 个关键词"; # 提前创建所有关键词对应的输出目录,构造pdfgrep多模式匹配参数 grep_args=() for kw in "${keywords[@]}"; do grep_args+=(-e "$kw") mkdir -p "$HOME/Sorted/$kw" done echo "开始扫描PDF目录..."; # 核心优化说明: # 1. 直接传入目录路径给pdfgrep,不使用Shell通配符展开,避免大目录下展开卡顿、参数过长报错 # 2. -l 仅输出匹配到的文件名,-i 忽略大小写,-r 递归遍历子目录 # 3. --include=*.pdf 仅扫描PDF后缀文件,跳过无关文件 # 4. -F 将关键词作为固定字符串匹配(若关键词为正则表达式请删除该参数),匹配速度比正则快30%以上 # 5. stdbuf -oL 强制pdfgrep使用行缓冲,匹配到结果立刻输出,解决日志卡顿问题 # 6. 逐行读取匹配结果,批量处理文件,减少不必要的进程开销 stdbuf -oL pdfgrep -lirF --include=*.pdf "${grep_args[@]}" "$HOME/PDFs/" | while read -r matched_file; do # 检查当前文件命中的关键词,复制到对应目录 for kw in "${keywords[@]}"; do if pdfgrep -qF "$kw" "$matched_file"; then # -n 参数不覆盖已存在的同名文件,需要覆盖请删除该参数 cp -n "$matched_file" "$HOME/Sorted/$kw/" fi done done echo "匹配任务执行完成"; echo "###########################"; echo "未匹配到任何文件的关键词:"; find "$HOME/Sorted/" -type d -empty -printf "%P\n"; find "$HOME/Sorted/" -type d -empty -delete;
额外说明
- 如果PDF是扫描生成的图片版PDF,
pdfgrep无法直接识别内容,需要先做OCR文字识别才能检索,和脚本逻辑无关。 - 如果关键词数量超过10个,可以把关键词单独写到一个文本文件里,用
pdfgrep -f 关键词文件路径的方式加载,比写在数组里更易维护。
内容的提问来源于stack exchange,提问作者Bart
相关产品推荐
相关产品推荐

