《Obsidian终极整理术!一键自动归类文件|Templater脚本分享》

用的是templater插件的模版
代码如下

<%*
// === 配置部分 ===
const 配置 = {
  模拟模式: true,           // true=仅预览,false=实际移动
  源目录: "",               // 如果留空,则扫描整个Vault;否则仅扫描该目录
  目标根目录: "Pages/【环境数据】(天气)", // 移动到的目录
  排除目录: ["Template", "Assets"],  // 排除的目录(无论全局/局部模式均生效)
  搜索关键词: "环境数据",     // 可改为其他关键词(如"临时"、"草稿")
  包含子目录文件: true,      // 是否包含子目录中的文件
  搜索范围: {
    文件名: true,     // 搜索文件名中的关键词
    正文内容: false,   // 搜索正文中的关键词
    元数据: false     // frontmatter,搜索YAML中的关键词
  }
};

// === 增强版文件检查函数 ===
async function 检查文件匹配(文件) {
  const 文件名 = 文件.name;
  const 文件路径 = 文件.path;
  
  // 基础检查
  if (!文件名.endsWith('.md')) return false;
  if (配置.排除目录.some(目录 => 文件路径.includes(`${目录}/`))) return false;
  if (文件路径.startsWith(配置.目标根目录)) return false;

  // 目录位置检查
  if (!配置.包含子目录文件) {
    if (配置.源目录) {
      if (文件.parent.path !== 配置.源目录) return false;
    } else {
      if (文件路径.includes('/')) return false;
    }
  } else if (配置.源目录 && !文件Path.startsWith(配置.源目录)) {
    return false;
  }

  // ★★★ 增强的搜索逻辑 ★★★
  const 搜索词 = 配置.搜索关键词;
  let 已匹配 = false;

  // 1. 先检查文件名(最快)
  if (配置.搜索范围.文件名 && 文件名.includes(搜索词)) {
    console.log(`[文件名匹配] ${文件路径}`);
    return true;
  }

  // 2. 如果需要检查内容,读取文件
  if (配置.搜索范围.正文内容 || 配置.搜索范围.元数据) {
    let 文件内容 = "";
    try {
      文件内容 = await app.vault.read(文件);
    } catch (e) {
      console.error(`读取文件失败: ${文件路径}`, e);
      return false;
    }

    // 检查元数据
    if (配置.搜索范围.元数据 && !已匹配) {
      const 元数据块 = 文件内容.match(/^---\s*[\s\S]*?---/);
      if (元数据块 && 元数据块[0].includes(搜索词)) {
        console.log(`[元数据匹配] ${文件路径}`);
        已匹配 = true;
      }
    }

    // 检查正文内容
    if (配置.搜索范围.正文内容 && !已匹配) {
      // 移除元数据块后检查
      const 纯正文 = 文件内容.replace(/^---\s*[\s\S]*?---/, '');
      if (纯正文.includes(搜索词)) {
        console.log(`[正文匹配] ${文件路径}`);
        已匹配 = true;
      }
    }
  }

  return 已匹配;
}

// === 主流程 ===
try {
  let 成功计数 = 0;
  let 失败计数 = 0;
  const 匹配文件列表 = [];
  const 所有Markdown文件 = app.vault.getMarkdownFiles();
  
  console.log("开始搜索文件...");
  for (const 文件 of 所有Markdown文件) {
    if (await 检查文件匹配(文件)) {
      匹配文件列表.push(文件);
    }
  }

  // 日志输出
  console.groupCollapsed(`▼ 匹配结果 ${匹配文件列表.length}个`);
  匹配文件列表.forEach(f => console.log(f.path));
  console.groupEnd();

  if (匹配文件列表.length === 0) {
    const 范围描述 = 配置.源目录 
      ? `目录 ${配置.源目录}${配置.包含子目录文件 ? '及其子目录' : ''}`
      : `${配置.包含子目录文件 ? '整个Vault' : 'Vault根目录'}`;
    return new Notice(`🔍 在${范围描述}中未找到匹配 "${配置.搜索关键词}" 的文件`);
  }

  // 处理文件移动
  for (const 文件 of 匹配文件列表) {
    const 新路径 = `${配置.目标根目录}/${文件.name}`;
    
    try {
      if (配置.模拟模式) {
        console.log(`[模拟] ${文件.path} → ${新路径}`);
        成功计数++;
      } else {
        let 实际路径 = 新路径;
        for (let i = 1; app.vault.getAbstractFileByPath(实际路径); i++) {
          实际路径 = 新路径.replace(/\.md$/, `_${i}.md`);
        }
        await app.fileManager.renameFile(文件, 实际路径);
        成功计数++;
      }
    } catch (e) {
      console.error(`移动失败: ${文件.path}`, e);
      失败计数++;
    }
  }

  // ★★★ 优化弹窗 ★★★
  new Notice([
    `📂 ${配置.模拟模式 ? '预览移动' : '移动完成'}`,
    `🔍 匹配到: ${匹配文件列表.length}个文件`,
    `✅ 成功: ${成功计数}`,
    ...(失败计数 > 0 ? [`❌ 失败: ${失败计数}`] : []),
    `目标位置: ${配置.目标根目录}`
  ].join('\n'), 8000);

} catch (e) {
  console.error('脚本执行错误:', e);
  new Notice([
    '💥 脚本错误',
    e.message.length > 30 ? e.message.slice(0, 30) + '...' : e.message
  ].join('\n'));
}
%>

我目前用的暂时还没bug,你们可以试试看

有插件 auto-note-mover,可以按各种规则自动或手动移动笔记文件。可以试用一下

感想:有的时候,如果不想折腾的话,可以通过搜索相关的插件来解决自己的需求。在通过折腾而解决自己需求后,还可以将相关的代码打包成 Obsidian 插件来帮助别人

1 个赞

看了下介绍

使用标签还是标题正则来作为移动规则,只能使用其中一种条件

感觉好像这个没我的模版好。
我的模版可以识别标题,YAML,正文。


我用ai写的模版,不晓得怎么打包成插件

如果你对插件开发有兴趣,可以点击查看如下的开发者文档

我的经验是在插件开发过程中,以 Webstorm 为 IDE,从 Github 分叉克隆示例插件项目,并且运用软件工程知识

全都不会……
我都是靠AI

自动归类文章这个好,试了下插件只能标签归类,对我没什么用。我还是用属性归类方便些,后面也可以叫AI帮我整个

我新搞了个配置文档

能够设置多个了

1 个赞