process_pdf_batch.sh 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381
  1. #!/bin/bash
  2. # filepath: process_pdf_batch.sh
  3. # ============================================================================
  4. # PDF 批量处理脚本
  5. # 功能: 批量调用 ppstructurev3_single_client.py 处理多个 PDF 文件
  6. # ============================================================================
  7. set -e # 遇到错误立即退出
  8. # ============================================================================
  9. # 默认配置
  10. # ============================================================================
  11. DEFAULT_SCRIPT="ppstructurev3_single_client.py"
  12. DEFAULT_BASE_DIR="/Users/zhch158/workspace/data/流水分析"
  13. DEFAULT_OUTPUT_SUBDIR="data_PPStructureV3_Results"
  14. DEFAULT_API_URL="http://10.192.72.11:8111/layout-parsing"
  15. DEFAULT_TIMEOUT=300
  16. # ============================================================================
  17. # 颜色定义
  18. # ============================================================================
  19. RED='\033[0;31m'
  20. GREEN='\033[0;32m'
  21. YELLOW='\033[1;33m'
  22. BLUE='\033[0;34m'
  23. NC='\033[0m' # No Color
  24. # ============================================================================
  25. # 帮助信息
  26. # ============================================================================
  27. usage() {
  28. cat << EOF
  29. ${BLUE}=============================================================================
  30. PDF 批量处理脚本
  31. ==============================================================================${NC}
  32. ${GREEN}用法:${NC}
  33. $0 [选项]
  34. ${GREEN}选项:${NC}
  35. -s, --script SCRIPT Python 脚本路径 (默认: ${DEFAULT_SCRIPT})
  36. -d, --base-dir DIR PDF 文件基础目录 (默认: ${DEFAULT_BASE_DIR})
  37. -o, --output-subdir SUBDIR 输出子目录名称 (默认: ${DEFAULT_OUTPUT_SUBDIR})
  38. -u, --api-url URL API 地址 (默认: ${DEFAULT_API_URL})
  39. -t, --timeout SECONDS 超时时间(秒) (默认: ${DEFAULT_TIMEOUT})
  40. -f, --file-list FILE PDF 文件列表文件路径
  41. -p, --pdf-list "PDF1,PDF2" PDF 文件列表(逗号分隔)
  42. -h, --help 显示此帮助信息
  43. ${GREEN}PDF 文件指定方式 (按优先级):${NC}
  44. 1. 通过 -f 参数指定文件列表
  45. 2. 通过 -p 参数直接指定 PDF 列表
  46. 3. 处理基础目录下所有 .pdf 文件
  47. ${GREEN}文件列表格式 (每行一个):${NC}
  48. 子目录名/文件名.pdf
  49. 完整路径/文件名.pdf
  50. ${GREEN}示例:${NC}
  51. # 1. 使用文件列表
  52. $0 -f pdf_list.txt
  53. # 2. 直接指定 PDF 列表
  54. $0 -p "A用户_单元格扫描流水.pdf,B用户_扫描流水.pdf"
  55. # 3. 处理指定目录下所有 PDF
  56. $0 -d "/path/to/pdfs"
  57. # 4. 完整参数示例
  58. $0 -s "./ppstructurev3_single_client.py" \\
  59. -d "/Users/zhch158/workspace/data/流水分析" \\
  60. -o "data_PPStructureV3_Results" \\
  61. -u "http://10.192.72.11:8111/layout-parsing" \\
  62. -t 600 \\
  63. -f "pdf_list.txt"
  64. ${YELLOW}文件列表示例 (pdf_list.txt):${NC}
  65. A用户_单元格扫描流水/A用户_单元格扫描流水.pdf
  66. A用户_单元格图片合成/A用户_单元格图片合成.pdf
  67. B用户_扫描流水/B用户_扫描流水.pdf
  68. 对公_招商银行图/对公_招商银行图.pdf
  69. EOF
  70. exit 0
  71. }
  72. # ============================================================================
  73. # 参数解析
  74. # ============================================================================
  75. SCRIPT="${DEFAULT_SCRIPT}"
  76. BASE_DIR="${DEFAULT_BASE_DIR}"
  77. OUTPUT_SUBDIR="${DEFAULT_OUTPUT_SUBDIR}"
  78. API_URL="${DEFAULT_API_URL}"
  79. TIMEOUT="${DEFAULT_TIMEOUT}"
  80. FILE_LIST=""
  81. PDF_LIST=""
  82. while [[ $# -gt 0 ]]; do
  83. case $1 in
  84. -s|--script)
  85. SCRIPT="$2"
  86. shift 2
  87. ;;
  88. -d|--base-dir)
  89. BASE_DIR="$2"
  90. shift 2
  91. ;;
  92. -o|--output-subdir)
  93. OUTPUT_SUBDIR="$2"
  94. shift 2
  95. ;;
  96. -u|--api-url)
  97. API_URL="$2"
  98. shift 2
  99. ;;
  100. -t|--timeout)
  101. TIMEOUT="$2"
  102. shift 2
  103. ;;
  104. -f|--file-list)
  105. FILE_LIST="$2"
  106. shift 2
  107. ;;
  108. -p|--pdf-list)
  109. PDF_LIST="$2"
  110. shift 2
  111. ;;
  112. -h|--help)
  113. usage
  114. ;;
  115. *)
  116. echo -e "${RED}错误: 未知参数 '$1'${NC}"
  117. echo "使用 -h 或 --help 查看帮助信息"
  118. exit 1
  119. ;;
  120. esac
  121. done
  122. # ============================================================================
  123. # 验证参数
  124. # ============================================================================
  125. echo -e "${BLUE}==============================================================================${NC}"
  126. echo -e "${BLUE}PDF 批量处理任务启动${NC}"
  127. echo -e "${BLUE}==============================================================================${NC}"
  128. echo ""
  129. # 检查脚本是否存在
  130. if [[ ! -f "${SCRIPT}" ]]; then
  131. echo -e "${RED}错误: Python 脚本不存在: ${SCRIPT}${NC}"
  132. exit 1
  133. fi
  134. echo -e "${GREEN}✓ Python 脚本:${NC} ${SCRIPT}"
  135. # 检查基础目录是否存在
  136. if [[ ! -d "${BASE_DIR}" ]]; then
  137. echo -e "${RED}错误: 基础目录不存在: ${BASE_DIR}${NC}"
  138. exit 1
  139. fi
  140. echo -e "${GREEN}✓ 基础目录:${NC} ${BASE_DIR}"
  141. echo -e "${GREEN}✓ 输出子目录:${NC} ${OUTPUT_SUBDIR}"
  142. echo -e "${GREEN}✓ API 地址:${NC} ${API_URL}"
  143. echo -e "${GREEN}✓ 超时时间:${NC} ${TIMEOUT} 秒"
  144. echo ""
  145. # ============================================================================
  146. # 构建 PDF 文件列表
  147. # ============================================================================
  148. declare -a PDF_FILES
  149. # 方式1: 从文件列表读取
  150. if [[ -n "${FILE_LIST}" ]]; then
  151. if [[ ! -f "${FILE_LIST}" ]]; then
  152. echo -e "${RED}错误: 文件列表不存在: ${FILE_LIST}${NC}"
  153. exit 1
  154. fi
  155. echo -e "${YELLOW}从文件列表读取: ${FILE_LIST}${NC}"
  156. while IFS= read -r line; do
  157. # 跳过空行和注释
  158. [[ -z "$line" || "$line" =~ ^# ]] && continue
  159. # 去除首尾空格
  160. line=$(echo "$line" | xargs)
  161. # 如果是相对路径,补充基础目录
  162. if [[ "$line" != /* ]]; then
  163. line="${BASE_DIR}/${line}"
  164. fi
  165. PDF_FILES+=("$line")
  166. done < "${FILE_LIST}"
  167. # 方式2: 从逗号分隔的列表读取
  168. elif [[ -n "${PDF_LIST}" ]]; then
  169. echo -e "${YELLOW}从参数列表读取 PDF 文件${NC}"
  170. IFS=',' read -ra PDFS <<< "$PDF_LIST"
  171. for pdf in "${PDFS[@]}"; do
  172. pdf=$(echo "$pdf" | xargs) # 去除空格
  173. # 如果是相对路径,补充基础目录
  174. if [[ "$pdf" != /* ]]; then
  175. # 检查是否包含子目录
  176. if [[ "$pdf" == */* ]]; then
  177. pdf="${BASE_DIR}/${pdf}"
  178. else
  179. # 尝试在基础目录的子目录中查找
  180. pdf_name="${pdf%.pdf}"
  181. pdf="${BASE_DIR}/${pdf_name}/${pdf}"
  182. fi
  183. fi
  184. PDF_FILES+=("$pdf")
  185. done
  186. # 方式3: 查找基础目录下所有 PDF
  187. else
  188. echo -e "${YELLOW}在基础目录中查找所有 PDF 文件${NC}"
  189. # 使用 find 命令查找所有 .pdf 文件
  190. while IFS= read -r -d $'\0' file; do
  191. PDF_FILES+=("$file")
  192. done < <(find "${BASE_DIR}" -type f -name "*.pdf" -print0 | sort -z)
  193. fi
  194. # 检查是否找到 PDF 文件
  195. if [[ ${#PDF_FILES[@]} -eq 0 ]]; then
  196. echo -e "${RED}错误: 未找到任何 PDF 文件${NC}"
  197. exit 1
  198. fi
  199. echo -e "${GREEN}找到 ${#PDF_FILES[@]} 个 PDF 文件:${NC}"
  200. for i in "${!PDF_FILES[@]}"; do
  201. echo -e " ${BLUE}[$((i+1))]${NC} ${PDF_FILES[$i]}"
  202. done
  203. echo ""
  204. # ============================================================================
  205. # 确认执行
  206. # ============================================================================
  207. read -p "$(echo -e ${YELLOW}"是否继续处理? [Y/n]: "${NC})" confirm
  208. confirm=${confirm:-Y}
  209. if [[ ! "$confirm" =~ ^[Yy]$ ]]; then
  210. echo -e "${YELLOW}已取消处理${NC}"
  211. exit 0
  212. fi
  213. echo ""
  214. # ============================================================================
  215. # 处理 PDF 文件
  216. # ============================================================================
  217. success_count=0
  218. failed_count=0
  219. skipped_count=0
  220. declare -a failed_files
  221. echo -e "${BLUE}==============================================================================${NC}"
  222. echo -e "${BLUE}开始批量处理${NC}"
  223. echo -e "${BLUE}==============================================================================${NC}"
  224. echo ""
  225. start_time=$(date +%s)
  226. for i in "${!PDF_FILES[@]}"; do
  227. pdf_file="${PDF_FILES[$i]}"
  228. current=$((i+1))
  229. total=${#PDF_FILES[@]}
  230. echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
  231. echo -e "${BLUE}[${current}/${total}] 处理: ${pdf_file}${NC}"
  232. echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
  233. # 检查文件是否存在
  234. if [[ ! -f "$pdf_file" ]]; then
  235. echo -e "${RED}⚠ 跳过: 文件不存在${NC}"
  236. ((skipped_count++))
  237. echo ""
  238. continue
  239. fi
  240. # 确定输出目录
  241. pdf_dir=$(dirname "$pdf_file")
  242. output_dir="${pdf_dir}/${OUTPUT_SUBDIR}"
  243. # 显示处理信息
  244. echo -e "${GREEN}📄 输入文件:${NC} $pdf_file"
  245. echo -e "${GREEN}📁 输出目录:${NC} $output_dir"
  246. echo -e "${GREEN}🌐 API 地址:${NC} $API_URL"
  247. echo ""
  248. # 创建输出目录
  249. # mkdir -p "$output_dir"
  250. # 执行处理
  251. echo -e "${YELLOW}开始处理...${NC}"
  252. file_start_time=$(date +%s)
  253. if python "${SCRIPT}" \
  254. --input_file "$pdf_file" \
  255. --output_dir "$output_dir" \
  256. --api_url "$API_URL" \
  257. --timeout "$TIMEOUT"; then
  258. file_end_time=$(date +%s)
  259. file_duration=$((file_end_time - file_start_time))
  260. echo -e "${GREEN}✓ 成功${NC} (耗时: ${file_duration}秒)"
  261. ((success_count++))
  262. else
  263. file_end_time=$(date +%s)
  264. file_duration=$((file_end_time - file_start_time))
  265. echo -e "${RED}✗ 失败${NC} (耗时: ${file_duration}秒)"
  266. ((failed_count++))
  267. failed_files+=("$pdf_file")
  268. fi
  269. echo ""
  270. done
  271. end_time=$(date +%s)
  272. total_duration=$((end_time - start_time))
  273. # ============================================================================
  274. # 输出统计信息
  275. # ============================================================================
  276. echo -e "${BLUE}==============================================================================${NC}"
  277. echo -e "${BLUE}处理完成${NC}"
  278. echo -e "${BLUE}==============================================================================${NC}"
  279. echo ""
  280. echo -e "${GREEN}📊 统计信息:${NC}"
  281. echo -e " 总文件数: ${total}"
  282. echo -e " ${GREEN}✓ 成功: ${success_count}${NC}"
  283. echo -e " ${RED}✗ 失败: ${failed_count}${NC}"
  284. echo -e " ${YELLOW}⊘ 跳过: ${skipped_count}${NC}"
  285. echo -e " ⏱️ 总耗时: ${total_duration} 秒 ($(date -u -r $total_duration +%H:%M:%S))"
  286. echo ""
  287. # 显示失败的文件
  288. if [[ ${failed_count} -gt 0 ]]; then
  289. echo -e "${RED}失败的文件:${NC}"
  290. for file in "${failed_files[@]}"; do
  291. echo -e " ${RED}✗${NC} $file"
  292. done
  293. echo ""
  294. fi
  295. # 生成日志文件
  296. log_file="batch_process_$(date +%Y%m%d_%H%M%S).log"
  297. {
  298. echo "批量处理日志"
  299. echo "============================================"
  300. echo "开始时间: $(date -r $start_time)"
  301. echo "结束时间: $(date -r $end_time)"
  302. echo "总耗时: ${total_duration} 秒"
  303. echo ""
  304. echo "统计信息:"
  305. echo " 总文件数: ${total}"
  306. echo " 成功: ${success_count}"
  307. echo " 失败: ${failed_count}"
  308. echo " 跳过: ${skipped_count}"
  309. echo ""
  310. if [[ ${failed_count} -gt 0 ]]; then
  311. echo "失败的文件:"
  312. for file in "${failed_files[@]}"; do
  313. echo " - $file"
  314. done
  315. fi
  316. } > "$log_file"
  317. echo -e "${GREEN}📝 日志已保存: ${log_file}${NC}"
  318. echo ""
  319. # 退出码
  320. if [[ ${failed_count} -gt 0 ]]; then
  321. exit 1
  322. else
  323. exit 0
  324. fi