#!/bin/bash # ============================================================================ # 程序名称: novel-format-check.sh # ============================================================================ # 功能描述: 小说写作规范检查模块,自动校验格式、字数、描写标准 # ============================================================================ # 设计目的: # 1. 检查章节格式规范(禁止Markdown标题、分隔线等) # 2. 校验字数标准(5000-8000字) # 3. 评估描写标准(内心独白、动作描写、对话、环境描写) # 4. 检查禁止项(沉默良久、省略号、过渡词) # 5. 生成检查报告并计算总分 # ============================================================================ # 输入参数: # $1: 章节文件名(可选,默认检查最新章节) # ============================================================================ # 输出: # 标准输出: 检查结果 # 文件: ${章节名}-检查报告.md # ============================================================================ # 依赖: # - grep, sed, wc, bc # - /root/.openclaw/workspace/平安书 目录 # ============================================================================ # 作者: 青龙 (OpenClaw AI) # 创建时间: 2026-02-13 # 更新: 2026-02-13 v1.1 修复逻辑错误 # ============================================================================ # 检查清单: # [x] 头部注释完整 # [x] grep错误处理 (2>/dev/null) # [x] 空值处理 ([ -z "$var" ]) # [x] 返回值处理 ($?) # [x] 语法检查 (bash -n) # ============================================================================ NOVEL_DIR="/root/.openclaw/workspace/平安书" TIMESTAMP=$(date '+%Y-%m-%d %H:%M') # ============================================================================ # 1. 检查格式规范 # ============================================================================ check_format() { local chapter="$1" local errors="" # 检查Markdown标题 if grep -q "^#" "$chapter" 2>/dev/null; then errors="${errors}❌ 包含Markdown标题\n" fi # 检查分隔线 if grep -q "^---" "$chapter" 2>/dev/null; then errors="${errors}❌ 包含分隔线\n" fi # 检查章节小节 if grep -q "^## " "$chapter" 2>/dev/null; then errors="${errors}❌ 包含章节小节\n" fi # 检查章节落款 if grep -qE "第[0-9]+章.*完" "$chapter" 2>/dev/null; then errors="${errors}❌ 包含章节落款\n" fi # 检查**加粗标记(纯文本禁止任何Markdown格式) if grep -q "\*\*" "$chapter" 2>/dev/null; then local count=$(grep -c "\*\*" "$chapter") errors="${errors}❌ 包含${count}处**加粗标记(纯文本禁止Markdown格式)\n" fi if [ -z "$errors" ]; then echo "✅ 格式规范: 通过" return 0 else echo "⚠️ 格式规范: 存在问题" echo -e "$errors" return 1 fi } # ============================================================================ # 2. 检查字数标准 # ============================================================================ check_length() { local chapter="$1" # 使用Python精确统计纯汉字数 # wc -m 统计所有字符(含标点),需要去除 local char_count=$(python3 -c " import sys with open('$chapter', 'r', encoding='utf-8') as f: content = f.read() # 只统计汉字 count = sum(1 for c in content if '\u4e00' <= c <= '\u9fff') print(count) " 2>/dev/null) [ -z "$char_count" ] && char_count=0 echo " 字数统计: $char_count 字" if [ $char_count -ge 5000 ] && [ $char_count -le 8000 ]; then echo " ✅ 字数标准: 符合 (5000-8000)" return 0 elif [ $char_count -lt 5000 ]; then local diff=$((5000 - char_count)) echo " ⚠️ 字数标准: 不足 (需+$diff字)" return 1 else local diff=$((char_count - 8000)) echo " ⚠️ 字数标准: 超出 (需-$diff字)" return 1 fi } # ============================================================================ # 3. 检查描写标准 # ============================================================================ check_description() { local chapter="$1" # 内心独白 local inner=$(grep -cE "想道|心中|暗自|默念|忖道" "$chapter" 2>/dev/null) [ -z "$inner" ] && inner=0 echo " 内心独白: $inner 处" # 动作描写关键词 local action=$(grep -cE "身形|身形一|伸手|抬手|迈步|纵身|疾驰|飞奔" "$chapter" 2>/dev/null) [ -z "$action" ] && action=0 echo " 动作描写: $action 处" # 对话 local dialogue=$(grep -c "「\|”" "$chapter" 2>/dev/null) [ -z "$dialogue" ] && dialogue=0 dialogue=$((dialogue / 2)) echo " 对话数量: $dialogue 段" # 环境描写 local env_count=$(grep -cE "天空|山林|月光|风声|树叶|流水" "$chapter" 2>/dev/null) [ -z "$env_count" ] && env_count=0 echo " 环境描写: $env_count 处" # 评分 local score=0 if [ $inner -ge 3 ]; then score=$((score + 25)) echo " ✅ 内心独白: 符合 (≥3)" else echo " ⚠️ 内心独白: 不足 (<3)" fi if [ $action -ge 3 ]; then score=$((score + 25)) echo " ✅ 动作描写: 符合 (≥3)" else echo " ⚠️ 动作描写: 不足 (<3)" fi if [ $dialogue -ge 4 ]; then score=$((score + 25)) echo " ✅ 对话数量: 符合 (≥4)" else echo " ⚠️ 对话数量: 不足 (<4)" fi if [ $env_count -ge 5 ]; then score=$((score + 25)) echo " ✅ 环境描写: 符合 (≥5)" else echo " ⚠️ 环境描写: 不足 (<5)" fi echo " 📊 描写评分: $score/100" if [ $score -ge 75 ]; then return 0 else return 1 fi } # ============================================================================ # 4. 检查禁止项 # ============================================================================ check_prohibited() { local chapter="$1" local violations=0 # 沉默良久 local silence=$(grep -c "沉默良久" "$chapter" 2>/dev/null) [ -z "$silence" ] && silence=0 if [ $silence -gt 2 ]; then echo " ⚠️ '沉默良久': $silence 次 (应≤2)" violations=$((violations + 1)) else echo " ✅ '沉默良久': $silence 次 (≤2)" fi # 省略号(中文省略号) local ellipsis=$(grep -c "……" "$chapter" 2>/dev/null) [ -z "$ellipsis" ] && ellipsis=0 if [ $ellipsis -gt 5 ]; then echo " ⚠️ '……': $ellipsis 次 (应≤5)" violations=$((violations + 1)) else echo " ✅ '……': $ellipsis 次 (≤5)" fi # 过渡段落 local transition=$(grep -cE "不多时|片刻之后|过了一会儿" "$chapter" 2>/dev/null) [ -z "$transition" ] && transition=0 if [ $transition -gt 3 ]; then echo " ⚠️ 过渡词: $transition 次 (应≤3)" else echo " ✅ 过渡词: $transition 次 (≤3)" fi # 返回0表示无违规,返回1表示有违规 if [ $violations -gt 0 ]; then return 1 else return 0 fi } # ============================================================================ # 5. 生成检查报告 # ============================================================================ generate_report() { local chapter="$1" local chapter_name=$(basename "$chapter" .txt) local report_file="${chapter%.txt}-检查报告.md" # 如果章节在 NOVEL_DIR 下,报告也要放到 NOVEL_DIR if [[ "$chapter" == "$NOVEL_DIR"* ]]; then local base_name=$(basename "$chapter" .txt) report_file="$NOVEL_DIR/${base_name}-检查报告.md" fi # 检查各项 local format_status=0 local length_status=0 local desc_status=0 local prohibited_status=0 check_format "$chapter" || format_status=1 echo "" check_length "$chapter" || length_status=1 echo "" check_description "$chapter" || desc_status=1 echo "" check_prohibited "$chapter" || prohibited_status=1 # 计算总分 local total=4 local passed=$((total - format_status - length_status - desc_status - prohibited_status)) local score=$((passed * 100 / total)) # 生成报告 cat > "$report_file" << MD # ${chapter_name} - 格式检查报告 **时间**: $TIMESTAMP **章节**: $chapter_name ## 检查结果 | 项目 | 状态 | 分值 | |------|------|------| | 格式规范 | $([ $format_status -eq 0 ] && echo "✅ 通过" || echo "❌ 失败") | $([ $format_status -eq 0 ] && echo "25" || echo "0") | | 字数标准 | $([ $length_status -eq 0 ] && echo "✅ 通过" || echo "❌ 失败") | $([ $length_status -eq 0 ] && echo "25" || echo "0") | | 描写标准 | $([ $desc_status -eq 0 ] && echo "✅ 通过" || echo "❌ 失败") | $([ $desc_status -eq 0 ] && echo "25" || echo "0") | | 禁止项 | $([ $prohibited_status -eq 0 ] && echo "✅ 通过" || echo "❌ 失败") | $([ $prohibited_status -eq 0 ] && echo "25" || echo "0") | **总分**: $score/100 $(if [ $score -ge 75 ]; then echo "## ✅ 检查结果: 通过" else echo "## ⚠️ 检查结果: 需要改进" fi) ## 改进建议 $(if [ $format_status -eq 1 ]; then echo "- 遵守写作标准程序,禁止Markdown标题、分隔线" fi) $(if [ $length_status -eq 1 ]; then echo "- 调整字数至5000-8000字范围" fi) $(if [ $desc_status -eq 1 ]; then echo "- 增加内心独白、动作描写、对话、环境描写" fi) $(if [ $prohibited_status -eq 1 ]; then echo "- 减少禁止使用的表达(沉默良久、省略号、过渡词)" fi) --- *自动生成于 $TIMESTAMP* MD echo "" echo "📁 报告: $report_file" echo "📊 得分: $score/100" } # ============================================================================ # 主程序 # ============================================================================ main() { echo "╔════════════════════════════════════════════════════╗" echo "║ 小说写作规范检查模块 v1.1 ║" echo "╚════════════════════════════════════════════════════╝" echo "" if [ -z "$1" ]; then # 检查最新章节 local latest=$(ls -t "$NOVEL_DIR"/*.txt 2>/dev/null | grep -E "[0-9]+章" | head -1) if [ -z "$latest" ]; then echo "ℹ️ 未发现章节文件" exit 1 fi echo "📖 检查最新章节: $(basename "$latest")" generate_report "$latest" else echo "📖 检查章节: $1" generate_report "$NOVEL_DIR/$1" fi echo "" echo "✅ 检查完成" } main "$@"