? ? ? ?Shutil是一個Python內置的用來高效處理文件和目錄遷移任務的庫。Shutil不僅支持基本的文件復制、移動和刪除操作,還具備處理大文件、批量遷移目錄、以及跨平臺兼容性等特性。通過使用Shutil,我們可以更加輕松地實現文件系統的管理和維護,本文我將講解該庫的使用方法以需要注意的地方。
目錄
?StrPath與BytesPath
?文件元數據(MetaData)
基礎元數據
?時間戳元數據
?權限元數據?
系統級元數據?
符號鏈接
符號鏈接基本含義
什么時候用符號鏈接?
復制文件
shutil.copy
可能出現的錯誤
shutil.copyfile
可能出現的錯誤
shutil.copy2
可能出現的錯誤
移動文件
shutil.move
可能出現的錯誤:
刪除目錄?
shutil.rmtree
總結:
?StrPath與BytesPath
????????在使用shutil內置的函數時,部分函數觀察其注釋可以發現傳入的路徑參數有兩種。分別是字符串路徑StrPath與BytesPath,這里我們來講一下二者的區別。
BytesPath是路徑的二進制表示形式,通常用于:
- 處理 ?非 UTF-8 編碼的文件名?(如某些特殊字符或非標準編碼的文件系統)
- 與底層操作系統 API 交互時(某些系統調用需要二進制格式的路徑)
其實就是字符串的字節碼,我們可以使用encode函數來將一個字符串轉換為字節碼
StrPath=r"E:\Desktop\lec01.pptx"
print(StrPath)
BytesPath=r"E:\Desktop\lec01.pptx".encode('utf-8')#utf-8是通用的編碼方式
print(BytesPath)
對于純英文路徑來說將encode為字節碼之后,即變為Bytes型路徑后其輸出結果為:b‘原路徑’
當路徑中含有非英文單詞時,將其encode為字節碼之后,?即變為Bytes型路徑后其輸出結果:
中文字符課程被encode為utf-?8類型的字節碼是'\xe8\xaf\xbe\xe7\xa8\x8b',其余部分保持不變
?文件元數據(MetaData)
????????文件元數據是描述文件屬性的數據,包含除文件內容本身外的所有信息。在Windows系統中可以通過右鍵文件點擊屬性在屬性面板中查看
基礎元數據
元數據類型 | 說明 | 查看方法(Python) |
---|---|---|
文件名 | 文件的名稱 | os.path.basename(filepath) |
文件大小 | 文件的字節大小 | os.path.getsize(filepath) |
文件類型 | 文件擴展名/格式 | os.path.splitext(filepath)[1] |
文件路徑 | 文件的完整存儲路徑 | os.path.abspath(filepath) |
?時間戳元數據
時間戳類型 | 說明 | Unix對應字段 | Windows對應字段 |
---|---|---|---|
創建時間(ctime) | 文件創建時間 | st_ctime | st_ctime |
修改時間(mtime) | 文件內容最后修改時間 | st_mtime | st_mtime |
訪問時間(atime) | 文件最后被訪問時間 | st_atime | st_atime |
元數據修改時間 | 文件屬性(非內容)最后修改時間(Unix特有) | st_ctime | - |
?權限元數據?
權限類型 | 說明 | Unix查看 | Windows查看 |
---|---|---|---|
文件模式 | 讀寫執行權限(rwx) | stat.st_mode | 文件屬性→安全選項卡 |
用戶ID(UID) | 文件所有者ID | stat.st_uid | - |
組ID(GID) | 文件所屬組ID | stat.st_gid | - |
訪問控制列表(ACL) | 更精細的權限控制 | getfacl 命令 | 文件屬性→安全→高級 |
系統級元數據?
元數據類型 | 說明 | 查看方法 |
---|---|---|
設備ID | 文件所在的設備標識符 | stat.st_dev |
inode編號 | 文件系統索引節點號(Unix) | stat.st_ino |
硬鏈接數 | 指向該文件的硬鏈接數量 | stat.st_nlink |
文件系統標志 | 文件特殊屬性(如只讀、隱藏等) | stat.st_flags (Unix) |
上述表格中提到的元數據都可以使用os庫來查看,具體代碼如下:?
import os
from datetime import datetime
s=os.stat(r'BG.py')
print("="*50)
print(f"大小: {s.st_size} 字節")
print(f"設備: {s.st_dev}")
print(f"inode: {s.st_ino}")
print(f"硬鏈接數: {s.st_nlink}")
print(f"權限: {oct(s.st_mode)}")
print(f"所有者UID: {s.st_uid}")
print(f"所屬組GID: {s.st_gid}")
print(f"創建時間: {datetime.fromtimestamp(s.st_ctime)}")
print(f"修改時間: {datetime.fromtimestamp(s.st_mtime)}")
print(f"訪問時間: {datetime.fromtimestamp(s.st_atime)}")
????????當然,shutil某些函數在對文件復制操作時也會涉及到對上述元數據的操作。
符號鏈接
?????在使用shutil的一些函數時,大家可能會看到follow_symlinks類似的參數,這里的symlinks指的是符號鏈接,所謂符號鏈接(Symbolic Link,也叫軟鏈接)就像電腦里的"快捷方式"或"替身",但它比普通的快捷方式更強大。
符號鏈接基本含義
????????使用一個不恰當的比喻, 符號鏈接其實就像是個書簽?📖。
????????想象你在看一本很厚的書,你在第1頁寫了個筆記:"重要內容見第500頁"。那么這個筆記其實就是一個符號鏈接:
- ?實際文件? = 第500頁的內容
- ?符號鏈接? = 第1頁的這個筆記
你通過這個"筆記"能直接找到真正的內容
???他的特點就是:
- 文件大小很小(就像書簽只占一點點位置)
- 刪除符號鏈接不會影響原文件(撕掉書簽不會刪除第500頁)
?這實際與我們安裝應用時創建的快捷方式類似,當然二者還是有一些區別的,以下是二者的區別:
特性 | 符號鏈接 | 普通快捷方式 |
---|---|---|
系統層級 | 文件系統級別 | 應用級別 |
兼容性 | 所有程序都能識別 | 部分程序識別 |
跨設備 | 可以指向網絡位置 | 通常只能本地 |
什么時候用符號鏈接?
-
?節省空間?:同一個大文件需要在多個位置使用
比如:你的電影庫實際存放在D盤,但在C盤的"我的影片"文件夾里創建鏈接
-
?版本切換?:快速切換不同版本軟件
比如:
python
?鏈接可以指向?python3.8
?或?python3.9
-
?系統維護?:不改動原有結構的情況下調整文件位置
復制文件
當我們使用shutil復制文件時,共有以下函數可以用來復制文件,他們的主要作用如下表所示:
函數名稱 | 功能描述 |
---|---|
shutil.copy(src, dst) | 復制文件到目標路徑(dst 可以是目錄或新文件名),?不保留文件元數據? |
shutil.copyfile(src, dst) | 僅復制文件內容,dst 必須是完整文件名且不能已存在,否則報錯 |
shutil.copytree(src, dst) | ?遞歸復制整個目錄樹?(包括子目錄),要求目標目錄dst 必須不存在? |
shutil.copy2(src, dst) | 功能同copy() ,但會保留文件元數據?(如修改時間、權限等) |
shutil.copymode(src, dst) | 僅復制文件的權限模式?(不復制內容或元數據) |
shutil.copyfileobj(fsrc, fdst) | 在文件對象級別復制內容(需手動打開文件對象) |
shutil.copystat(src, dst) | 僅復制文件的元數據?(權限、時間戳等),?不復制文件內容 |
shutil.copy
參數詳解:
參數 | 含義 | 類型 |
---|---|---|
src | 待復制的源文件路徑字符串或Bytes字節碼路徑(必須是文件名) | str or bytes str |
dst | 目標路徑的字符串或Bytes字節碼路徑(可以是文件夾也可以是文件名) | str or bytes str |
follow_symlinks | 復制符號鏈接指向的實際文件內容還是復制符號鏈接本身 | bool |
代碼:
????????需要注意的是shutil.copy只會復制文件內容,并不復制元數據,像修改時間等這些元數據都是運行代碼后產生的
src路徑下文件:
import os
import shutil
shutil.copy(src=r"E:\Desktop\文件保存\26162605op6m.pdf",dst=os.getcwd())
運行代碼后dst路徑下文件:?
可以看到最近修改時間變成了實際運行代碼時間?
可能出現的錯誤
Permission Error(dst路徑下的文件已被打開):
??????需要注意的是當dst路徑是文件且該文件已經被打開再嘗試使用copy函數時,會出現Permission Error
dst路徑下的文件已被打開,出現Permission Error異常
shutil.copyfile
????????shutil.copyfile與shuitl.copy函數唯一不同的地方在于它的dst路徑必須是文件名,而shutil.copy則既可以是目錄名也可以是文件名。這里dst路徑下的文件名的內容空白或名字重復無所謂。當然,shutil.copyfile函數也只會復制文件內容,并不復制元數據,像修改時間等這些元數據都是運行代碼后產生的。
參數詳解:
參數 | 含義 | 類型 |
---|---|---|
src | 待復制的源文件路徑字符串(必須是文件名) | str |
dst | 目標路徑的字符串(必須是文件名且不能已存在) | str? |
follow_symlinks | 復制符號鏈接指向的實際文件內容還是復制符號鏈接本身 | bool |
代碼:
src路徑下文件:
import os
import shutil
#選擇移動到當前工作目錄下
dst=os.path.join(os.getcwd(),"小謝的selenium操作手冊.docx")
shutil.copyfile(src=r"E:\Desktop\測試開發\小謝的selenium操作手冊.docx",dst=dst)
運行代碼后dst路徑下文件:?
可以看到最近修改時間變成了實際運行代碼時間?
可能出現的錯誤
?Permission Error(dst路徑下的文件已被打開):
??????需要注意的是當dst路徑下的文件已經被打開再嘗試使用copyfile函數時,會出現Permission Error。
dst路徑下的文件已被打開,出現Permission Error異常
shutil.copy2
????????shutil.copy2函數的參數與shutil.copy完全一致,二者唯一的區別是shutil.copy2會一同復制元數據。
?參數詳解:
參數 | 含義 | 類型 |
---|---|---|
src | 待復制的源文件路徑字符串或Bytes字節碼路徑(必須是文件名) | str or bytes str |
dst | 目標路徑的字符串或Bytes字節碼路徑(可以是文件夾也可以是文件名) | str or bytes str |
follow_symlinks | 復制符號鏈接指向的實際文件內容還是復制符號鏈接本身 | bool |
代碼:
src路徑下文件:
import os
import shutil
#選擇移動到當前工作目錄下
dst=os.path.join(os.getcwd(),"小謝的selenium操作手冊.docx")
shutil.copy2(src=r"E:\Desktop\測試開發\小謝的selenium操作手冊.docx",dst=dst)
運行代碼后dst路徑下文件:?
元數據一模一樣(這里只看修改時間便可以看出)。?
可能出現的錯誤
Permission Error(dst路徑下的文件已被打開):
??????需要注意的是當dst路徑是文件且該文件已經被打開再嘗試使用copy函數時,會出現Permission Error。
dst路徑下的文件已被打開,出現Permission Error異常
?shutil.copytree
????????shutil.copytrees是用來將一個文件夾下的所有內容復制到另一個文件夾中的函數。
參數詳解:
參數名稱 | 功能描述 |
---|---|
src | 必需參數,表示源目錄的路徑(字符串類型)。 |
dst | 必需參數,表示目標目錄的路徑(字符串類型)。 |
symlinks | 可選參數,布爾值(默認為?False )。若為?True ,則復制符號鏈接本身;若為?False ,則復制鏈接指向的實際內容。 |
ignore | 可選參數,可指定一個函數(如?shutil.ignore_patterns ),用于過濾不需要復制的文件或目錄。 |
copy_function | 可選參數,指定用于復制的函數(默認為?shutil.copy2 ,保留元數據)。也可替換為?shutil.copy ?等。 |
ignore_dangling_symlinks | 可選參數,布爾值(默認為?False )。若為?True ,則跳過無效的符號鏈接而不拋出異常。 |
dirs_exist_ok | 可選參數,布爾值(默認為?False )。若為?True ,允許目標目錄已存在(Python 3.8+ 新增)。 |
代碼:
src路徑下文件:
import shutil
'''
dir_exist_ok設置為True,
此時無論dst路徑下的文件夾是否存在
都將被復制到dst路徑下的文件夾內
'''
shutil.copytree(src=r"E:\Desktop\Videos",dst=r"E:\Desktop\test",dirs_exist_ok=True)
可能出現的錯誤
?NotADirectoryError(src路徑下的內容非文件夾)
當src路徑下的內容不是文件夾時會產生這個錯誤
?FileExitsError(dst下的文件夾已存在)
????????dst路徑下的文件夾存在且dir_exists_ok參數未設置為True,會產生這個錯誤
移動文件
shutil.move
參數詳解:
參數 | 含義 | 類型 |
---|---|---|
src | 待復制的源文件字符串(可以是文件名也可以是文件夾) | str? |
dst | 目標路徑的字符串(可以是文件名也可以是文件夾) | str |
copy_funciton | 復制文件回調函數,默認是shuti.copy2 | callable |
????????需要注意的是src是文件夾時,dst也必須是一個文件夾,因為我們不能把一個文件夾移動到一個文件里去。
代碼:?
import os
import shutil
shutil.move(src=r"E:\Desktop\文件保存\.docx",dst=os.getcwd())
????????關于copy_function這一參數的說明:
shutil.move函數文檔注釋內容?
????????所謂移動文件其實就是先把文件復制到目標路徑下,然后再把原路徑下的文件刪除。而將文件復制到目標路徑下這一功能無非就是使用shutil.copy,shutil.copy2等函數來實現,正如上邊注釋所言:可選的 'copy_function' 參數是將要使用的可調用復制源,否則它將被委托給 'copytree'。默認情況下,使用 copy2()
可能出現的錯誤:
Permission Error(src路徑下的文件已被打開):
????????在任何操作系統上我們都無法對一個已經被打開的文件進行遷移操作,如果src路徑下的文件被打開,那么自然也會拋出PermissionError的錯誤。
Error(dst路徑下已經有一個與src傳入路徑下同一個basename的文件)
dst路徑下有一個名為test.docx的文件
提示test.docx already exists?
ErrorCannot move a directory into itself) :
????????當src與dst二者存在包含關系,且src為父目錄時,便會產生這個錯誤。這里,文件保存是Desktop內的一個文件夾,當我們嘗試將Desktop移動到這個文件夾時便會出現這個錯誤。
FileExitsError(src路徑為文件夾,dst下的路徑為文件時)
????????src是一個文件夾,dst是一個文件,當我們嘗試將一個文件夾移動到文件中時便會產生錯誤
刪除目錄?
????????shutil.rmtree是shutil模塊中唯一用來執行刪除功能的函數,當然可以用來刪除文件的函數還有很多,比如os模塊下的os.remove,os.rmdir,下表給出了這幾三者之間的區別:
函數 | 能刪文件 | 能刪空目錄 | 能刪非空目錄 | 適用場景 |
---|---|---|---|---|
shutil.rmtree() | ? 不能 | ?? 可以 | ?? 可以 | ?僅用于刪除目錄樹(徹底刪除內部所有文件夾及內容)? |
os.remove() | ?? 可以 | ? 不能 | ? 不能 | ?僅用于刪除單個文件? |
os.rmdir() | ? 不能 | ?? 可以 | ? 不能 | ?僅用于刪除空目錄? |
shutil.rmtree
參數詳解:
參數 | 類型 | 默認值 | 功能描述 |
---|---|---|---|
?path | str | 必填 | 要刪除的目錄路徑,需要注意的是路徑必須是目錄且不為空 |
?ignore_errors | bool | False | 是否忽略刪除過程中可能產生的各種錯誤 |
onerror | callable | None | 錯誤處理回調函數 |
?dir_fd | int | None | 目錄文件描述符(僅Unix) |
?onexc ?(Python 3.12+) | callable | None | 異常處理回調函數(新版替代 |
在該文件夾下,我創建了3層深度的文件夾,并在內部新建了一個txt類型文件?
代碼 :
import shutil
shutil.rmtree(r"E:\Desktop\rmtree測試",ignore_errors=True)
運行代碼后,該文件夾被徹底刪除!但是os.remove和os.remdir對此卻無能為力
總結:
????????shutil是一個python內置的用來進行文件遷移的模塊,該模塊本質上是對os模塊處理文件相關操作的二次開發,比如shutil.copyfile函數,其實就是在使用with open語句進行兩次io操作:先讀取src內容,再將讀取內容寫入到dst路徑。
? ? ? ? 類似這樣的操作我們自己也可以寫出來,只不過并不一定會考慮到各種可能存在的異常情況以及跨平臺兼容性等問題,而shutil則已經將這些情況全部考慮并封裝成為了‘’輪子‘’供我們直接調用,快捷而方便。當然,這也是Python的特色之一。
? ? ? ??我想要駕駛一輛汽車,難道還需要自己制造輪胎嗎?