作者:袁首京 原創文章,轉載時請保留此聲明,並給出原文連接。 草堂南澗邊,有客嘯雲煙。 掃葉林風後,拾薪山雨前。 野橋通竹徑,流水入芝田。 琴月相親夜,更深戀不眠。 話說周世宗顯德年間,有位老先生,性情疏野,不以榮宦為意。一生遇見了很多人、經歷了許多事。可惜這些事我一件也不知道、這些人我一個也不曉得 ...
作者:袁首京
原創文章,轉載時請保留此聲明,並給出原文連接。
草堂南澗邊,有客嘯雲煙。
掃葉林風後,拾薪山雨前。
野橋通竹徑,流水入芝田。
琴月相親夜,更深戀不眠。
話說周世宗顯德年間,有位老先生,性情疏野,不以榮宦為意。一生遇見了很多人、經歷了許多事。可惜這些事我一件也不知道、這些人我一個也不曉得。所以以下內容除了這個開頭,通篇與這首詩、這個人都毫無關係。
只不過是前天隨意翻了一下我的移動硬碟,存的東西那叫一個亂。很多東西在不同的目錄或者是相似的目錄多次出現,也不知道哪個是新的、哪個是老的,哪個是有用的、哪個是沒用的,動也不敢動、刪也不敢刪,越看越鬧心、越不順眼。是時候該整理一下了。
但是真要動手,就發現這個事兒並不有趣。單單是比較兩個文件,就很浪費時間和精力。難道要一個個打開來看?很明顯就不現實。我要有這麼勤快,硬碟也不會亂到這種程度。只好寫一段程式來處理了。
用什麼寫呢?這種事兒,Windows 上bat
或powershell
、Linux 上 shell
可能就很合適,只不過這些我都不熟悉。我用的是 python
,幾十行代碼就基本可以使用了。最終效果如下:
你可能註意到,最終生成的是一個命令行程式 file_organizer.exe
(文末附下載鏈接)。回頭有空也可以寫個圖形界面的,不過一回頭通常就不知道回到啥時候了,管它呢,總之回頭再說。眼下先把已經寫過的這幾十行代碼交待一下。
需求分析
簡單的說,文件整理這個事兒可以分解為分類和清理兩個過程。大概需要回答以下問題:
- 要從哪個文件夾里挑選文件?
- 挑選哪種類型的文件,文檔、表格、圖片還是別的?
- 挑出的文件放在哪個文件夾?
- 挑出的文件放入新文件夾時,如果發現新文件夾中已經有同名文件了,該怎麼處理?
- 文件放到新文件夾後,原來的文件夾中還保留不保留?
介面定義
問題弄明白,事兒就好幹了。根據上述五個問題,我們可以給出如下的函數定義:
def organize(src: str, exts: str, dst: str, copy: bool = True, strategy: str):
"""
:param src:str 源路徑
:param exts:str 擴展名
:param dst:str 目的路徑
:param copy:bool 複製文件還是移動文件到目的路徑
:param strategy:str 重名處理策略
"""
pass
上述定義中,源路徑和目的路徑都是目錄。目錄可能有很多層級,我們需要遍歷其中的每一個文件,如果發現文件的擴展名,是需要處理的類型,則按策略對其採取遷移到目的路徑的操作。這個操作會在遍歷的過程中反覆執行,因此可以針對它再定義如下函數:
def deal(src: Path, dst: Path, copy: bool = True, strategy: str = "both"):
"""
:param src:Path 源路徑
:param dst:Path 目的路徑
:param copy:bool 複製文件還是移動文件到目的路徑
:param strategy:str 重名處理策略
"""
pass
其它還有不少操作,其中之一是假如遇到同名文件,如何判定它們是一樣的?方法有很多,比如對比它們的 md5sum 結果等等。Python 標準庫中有個 filecmp 就是處理這件事兒的,所以我們不用計算 md5sum 了。除此之外似乎沒什麼需要特別關註的點了。
編碼實現
事兒不大,不啰嗦了。以下就是核心代碼了:
import shutil
from loguru import logger
from os import walk, listdir
from pathlib import Path
from filecmp import cmp
def rename(file_name: str) -> str:
idx = file_name.index(".")
return file_name[:idx] + "_1" + file_name[idx:]
def deal(src: Path, dst: Path, copy: bool = True, strategy: str = "both"):
if not dst.exists():
shutil.copy2(src, dst)
logger.info("遷移原文件到 {}", dst)
elif cmp(src, dst):
logger.info("無需遷移文件 {}", dst)
elif strategy == "later" and src.stat().st_mtime > dst.stat().st_mtime:
shutil.copy2(src, dst)
logger.info("遷移新文件到 {}", dst)
elif strategy == "bigger" and src.stat().st_size > dst.stat().st_size:
shutil.copy2(src, dst)
logger.info("遷移大文件到 {}", dst)
else:
dst = dst.parent / Path(rename(dst.name))
shutil.copy2(src, dst)
logger.info("重命名文件到 {}", dst)
if not copy:
src.unlink()
def organize(src: str, exts: str, dst: str, copy: bool = True, strategy: str = "both"):
if not exts or len(exts) < 1:
raise ValueError('"exts" is invalid')
lc_exts = [e.lower() for e in exts.split()]
src_path = Path(src)
dst_path = Path(dst)
if not src_path.exists() or not src_path.is_dir():
raise ValueError('"src" is invalid')
if not dst_path.exists():
dst_path.mkdir(parents=True, exist_ok=True)
for root, _, files in walk(src):
for name in files:
file = Path(root) / Path(name)
if file.suffix.lower() not in lc_exts:
continue
target = dst_path / Path(name)
deal(file, target, copy, strategy)
if not copy and len(listdir(src_path)) < 1:
src_path.rmdir()
除此之外,就是些輔助代碼了,例如命令行幫助等等。此處不再羅列,我放在了 Github 上,需要的話可以去看。https://github.com/yuanshoujing/file_organizer
下載
最後,如果你需要的話,可以點擊如下鏈接下載打包的命令行程式:
此程式只能在 win10 以上系統中運行,並且沒有經過嚴肅測試,請謹慎使用,出了問題概不負責。
作者:袁首京原創文章,轉載時請保留此聲明,並給出原文連接。