Typora 笔记迁移 Obsidian 图片附件库批量移动方法,适用于笔记整理。
附件:Typora 笔记迁移 Obsidian 图片附件库批量移动程序
需求
在ypora 笔记迁移 Obsidian 图片附件库过程中,之前的笔记都是采用的相对路径方法,而现在准备采用wiki索引方法,因此得检查不能是否冲突,对文件夹(含子文件夹)内的很多“Database”的附件文件夹进行操作。
将所有的“Database”内的所有文件移动到“DatabaseNew”中,如果遇到名称重复的。在该目标文件夹下自动新建的“Database重复文件手动处理”用于存放重复文件。将重复的文件移动到“Database重复文件手动处理”文件夹中。
思路
-
文件聚合阶段:遍历所有名为 "Database" 的文件夹,将其中的文件移动到统一的新位置,并隔离出重名文件。
-
清理阶段:删除所有已经变为空的 "Database" 文件夹,并报告所有未能清空的异常情况。
实践
备份整个目标文件夹,打压缩!拷贝该文件夹副本进行操作。
参考目录
|-- 转换
| |-- organize_db.py
| `-- 综合软件使用教程 # 目标文件夹
| |-- Database
安装python,网上教程很多,不再赘述。
-
保存脚本: 将下面的代码保存为一个
.py文件,例如organize_db.py。 -
运行脚本: 打开终端或命令行工具,进入你保存脚本的目录,然后运行它:
python .\organize_db.py程序会提示您输入笔记库的文件夹路径。只需将文件夹的绝对路径复制粘贴进去,然后按回车键即可。
![[Pasted image 20251001134623.png]] -
检查结果: 脚本会自动扫描该文件夹及其所有子文件夹中的
Database文件,将其中的文件移动到统一的新位置,并隔离出重名文件。可以在Database重复中查看是否有重复文件。
![[Pasted image 20251001134644.png]]
代码
import os
import sys
import shutil
def organize_database_folders(root_folder):
"""
1. 将所有 'Database' 子文件夹内的文件统一移动到 'DatabaseNew'。
2. 重名文件则移动到 'Database重复文件手动处理'。
3. 删除所有变为空的 'Database' 文件夹。
4. 报告未能删除的非空 'Database' 文件夹。
你可以进行替换Database为你要操作的文件夹!
"""
# 3. 在目标文件夹下自动新建用于存放文件的文件夹
consolidate_dir = os.path.join(root_folder, "DatabaseNew")
duplicate_dir = os.path.join(root_folder, "Database重复文件手动处理")
try:
os.makedirs(consolidate_dir, exist_ok=True)
os.makedirs(duplicate_dir, exist_ok=True)
print(f"✔️ 目标文件夹创建/确认成功:\n - {consolidate_dir}\n - {duplicate_dir}")
except OSError as e:
print(f"❌ 错误: 无法创建目标文件夹,请检查权限。 {e}", file=sys.stderr)
return
moved_count = 0
duplicate_count = 0
# --- 阶段 1: 文件聚合 ---
print("\n--- 开始阶段 1: 移动文件 ---")
# 2. 扫描文件夹及文件
for dirpath, dirnames, filenames in os.walk(root_folder):
# 我们只关心名为 'Database' 的文件夹
if os.path.basename(dirpath) == "Database":
print(f"\n正在处理文件夹: {dirpath}")
for filename in filenames:
source_path = os.path.join(dirpath, filename)
dest_path_consolidate = os.path.join(consolidate_dir, filename)
# 4. 检查 'DatabaseNew' 中是否已存在同名文件
if not os.path.exists(dest_path_consolidate):
# 不存在重名,移动到 'DatabaseNew'
try:
shutil.move(source_path, dest_path_consolidate)
print(f" -> 移动到 'DatabaseNew': {filename}")
moved_count += 1
except Exception as e:
print(f" ❌ 移动失败 (权限问题?): {filename} - {e}")
else:
# 存在重名,移动到 'Database重复文件手动处理'
dest_path_duplicate = os.path.join(duplicate_dir, filename)
try:
shutil.move(source_path, dest_path_duplicate)
print(f" ⚠️ 发现重复, 移动到 '重复文件' 文件夹: {filename}")
duplicate_count += 1
except Exception as e:
print(f" ❌ 移动重复文件失败: {filename} - {e}")
# --- 阶段 2: 清理空文件夹 ---
print("\n--- 开始阶段 2: 清理空的 'Database' 文件夹 ---")
deleted_folders_count = 0
non_empty_folders = []
# 使用 topdown=False, 从子文件夹向上遍历,这样才能成功删除空目录
for dirpath, _, _ in os.walk(root_folder, topdown=False):
# 5. 检查目标文件夹(含子文件夹),删除所有空的“Database”文件夹
if os.path.basename(dirpath) == "Database":
try:
# os.listdir() 会列出所有文件和子文件夹
if not os.listdir(dirpath):
os.rmdir(dirpath)
print(f" 🗑️ 删除空文件夹: {dirpath}")
deleted_folders_count += 1
else:
# 如果文件夹移动后仍不为空,记录下来
non_empty_folders.append(dirpath)
except OSError as e:
print(f" ❌ 删除文件夹失败: {dirpath} - {e}")
non_empty_folders.append(dirpath)
# --- 最终报告 ---
print("\n--- 操作摘要 ---")
print(f"移动到 'DatabaseNew' 的文件总数: {moved_count}")
print(f"因重名而移动到 'Database重复文件手动处理' 的文件总数: {duplicate_count}")
print(f"成功删除的空 'Database' 文件夹数量: {deleted_folders_count}")
if non_empty_folders:
print("\n!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!")
print("!!! 警告: 以下 'Database' 文件夹未能清空或删除,请手动检查。")
print("!!! 原因可能是权限不足,或文件夹内含有无法处理的隐藏文件/子文件夹。")
for path in non_empty_folders:
print(f" - {path}")
print("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!")
else:
print("\n所有 'Database' 文件夹均已成功处理并清理。")
# --- 主程序入口 ---
if __name__ == "__main__":
target_folder = input("请输入要整理的根文件夹绝对路径并按 Enter: ")
target_folder = target_folder.strip().strip('"')
if os.path.isdir(target_folder):
print("-" * 60)
print(f"目标文件夹: {target_folder}")
print("此操作将执行以下任务:")
print(" 1. 聚合所有 'Database' 文件夹内的文件到 'DatabaseNew'。")
print(" 2. 隔离重名文件到 'Database重复文件手动处理'。")
print(" 3. 删除所有清理完毕的空 'Database' 文件夹。")
print("\n警告: 文件移动是不可逆操作,建议在继续前备份您的文件夹。")
print("-" * 60)
confirm = input("确认要开始整理吗? (y/n): ")
if confirm.lower() == 'y':
organize_database_folders(target_folder)
print("\n所有操作已执行完毕。")
else:
print("操作已由用户取消。")
else:
print(f"错误: 您输入的路径 '{target_folder}' 不是一个有效的文件夹。", file=sys.stderr)
-
创建目标目录: 首先使用
os.makedirs(exist_ok=True)创建DatabaseNew和Database重复文件手动处理文件夹,exist_ok=True参数确保了即使文件夹已存在,程序也不会报错。 -
第一次遍历 (文件移动): 使用
os.walk遍历所有文件夹。当找到一个名为Database的目录时,就处理其中的所有文件。-
通过
os.path.exists检查文件在DatabaseNew中是否已有同名文件。 -
根据检查结果,使用
shutil.move将文件移动到对应的目标位置。shutil.move比os.rename更强大,可以跨磁盘移动文件。
-
-
第二次遍历 (目录清理): 再次使用
os.walk,但这次加上了topdown=False参数。这使得遍历顺序从最深层的子目录开始,向上返回到根目录。这是删除空文件夹的关键,因为你必须先清空子目录才能删除父目录。-
通过
os.listdir(dirpath)检查文件夹是否为空。 -
如果为空,使用
os.rmdir(dirpath)将其安全删除。此函数只能删除空目录,如果目录非空会报错,增加了安全性。 -
如果目录非空或删除失败,就将其路径记录下来,在最后统一报告。
-
-
统一报告: 在所有操作结束后,打印清晰的总结报告,让用户了解操作结果,特别是需要手动干预的异常情况。
注意事项
本文来自博客园,作者:舟清颺,转载请注明原文链接:https://www.cnblogs.com/zqingyang/p/19122419

解决了Typora 笔记迁移 Obsidian 图片附件库批量移动问题,在ypora 笔记迁移 Obsidian 图片附件库过程中,之前的笔记都是采用的相对路径方法,而现在准备采用`wiki`索引方法,因此得检查不能是否冲突,对文件夹(含子文件夹)内的很多“Database”的附件文件夹进行操作。
将所有的“Database”内的所有文件移动到“DatabaseNew”中,如果遇到名称重复的。在该目标文件夹下自动新建的“Database重复文件手动处理”用于存放重复文件。将重复的文件移动到“Database重复文件手动处理”文件夹中。
浙公网安备 33010602011771号