Python利用shutil實現拷貝文件功能

楔子

shutil 是一個 Python 內置模塊,該模塊對文件的復制、刪除和壓縮等操作都提供瞭非常方便的支持。

下面來詳細介紹一下該模塊的用法。

chown:更改指定路徑的所有者用戶(組)

函數原型:

shutil.chown(path, user=None, group=None)

參數含義如下:

  • path:指定要操作的路徑;
  • user:指定所有者,可以是系統用戶名或者 UID,如果用戶不存在則報錯 "沒有此用戶";
  • group:表示組

該方法隻適用於 Unix 系統,下面演示一下。

>>> import shutil
>>> import pwd  # Unix
>>> import os
>>> 
>>> uid = os.stat("/home/lighthouse").st_uid
>>> pwd.getpwuid(uid)
pwd.struct_passwd(pw_name='lighthouse', 
                  pw_passwd='x', 
                  pw_uid=1000, 
                  pw_gid=1000, 
                  pw_gecos='', 
                  pw_dir='/home/lighthouse', 
                  pw_shell='/bin/bash')

# 將所有者改成 root
>>> shutil.chown("/home/lighthouse", user="root")
# 再次查看,發現所有者已經被修改瞭
>>> uid = os.stat("/home/lighthouse").st_uid
>>> pwd.getpwuid(uid)
pwd.struct_passwd(pw_name='root', 
                  pw_passwd='x', 
                  pw_uid=0, 
                  pw_gid=0, 
                  pw_gecos='root', 
                  pw_dir='/root', 
                  pw_shell='/bin/bash')

copy:復制文件

copy 函數可以將一個文件復制為另一個文件。

函數原型:

shutil.copy(src, dst, *, follow_symlinks=True)

參數含義如下:

  • src:文件的路徑,註意:必須是文件,如果是目錄則報出權限錯誤;
  • dst:文件或目錄的路徑,如果是一個已經存在的目錄,那麼會將 src 拷貝到該目錄中;否則會創建相應的文件;
  • follow_symlinks:表示是否遵循符號鏈接,默認為 True。如果為 True 則復制文件,為 False、並且 src 為軟連接,則創建一個新的軟連接;

該函數會返回目標路徑,即新創建的文件的路徑。

import shutil

shutil.copy("1.txt", "test")

如果 test 存在並且是目錄,那麼將 1.txt 拷貝到 test 目錄中;如果 test 不存在,那麼創建一個名為 test 的文件,內容和 1.txt 一致;如果 test 存在並且不是目錄,那麼會把已存在的 test 文件覆蓋掉,此時需要具備對 test 的寫權限,否則會報出權限錯誤:PermissionError。

另外使用 copy 復制文件時,文件的元信息(創建時間、修改時間)不會被保留,相當於創建瞭新文件。如果要保留文件的元信息,需要使用 copy2 函數(和 copy 函數用法一致,區別就是前者不保留文件元信息、後者會保留)。

copyfile:復制文件

參數和 copy、copy2 完全一致,隻不過 copyflle 的 dst 如果已存在,那麼必須是文件。

# 如果 test 存在並且是目錄,會報錯
# PermissionError: [Errno 13] Permission denied: 'test'
shutil.copyfile("1.txt", "test")

# 如果 test 不存在
# 那麼會創建一個名為 test 的文件,內容和 1.txt 一致
# 如果 test 存在並且不是目錄,那麼會把原來的文件覆蓋掉
shutil.copyfile("1.txt", "test")

比較簡單,可以自己試一下,所以 copy 要比 copyfile 更高級一些。copyfile 要求 dst 存在時必須是文件,而 copy 則允許 dst 是目錄,會自動將文件拷貝到目錄中。

使用 copyfile 同樣需要寫權限,並且 src 和 dst 不能是同一個文件,否則會報錯:SameFileError。

除瞭 copyfile 之外,還有一個更加低級的 copyfileobj。copyfileobj 也是拷貝,接收三個參數:fsrc、fdst、length,前兩個參數和 copy 類似,隻不過 fsrc 和 fdst 都必須是打開的文件對象,從名字上也能看出。至於第三個參數 length 表示緩沖區,默認是 16 * 1024 字節,如果為負數代表不走緩沖區,而是直接復制。

import shutil
from io import StringIO

buf1 = StringIO()
buf2 = StringIO()

# buf1 裡面寫入一些內容
buf1.write("古明地覺")
# 調整指針,移到開頭,否則讀取不到內容
buf1.seek(0)
# 將 buf1 的內容拷貝到 buf2 中
shutil.copyfileobj(buf1, buf2)
# 查看 buf2 的內容
print(buf2.getvalue())  # 古明地覺

雖然 copyfileobj 比較低級,但是它的速度也更快。當復制大文件時,采用 copyfileobj 會更有效率,復制小文件則使用 copyfile 會更方便一些。

copymode:復制權限位

參數和 copy 函數也完全相同,隻不過它是將一個文件的權限復制給另一個文件。比如 A 文件是隻讀,那麼復制給 B 之後 B 也是隻讀,但是 A 的內容不會復制給 B,因為 copymode 隻是復制權限。

除瞭 copymode 還有一個 copystat,參數也是一樣的,隻不過它除瞭復制權限之外還復制最後訪問時間、最後修改時間等元信息,可以自己試一下這兩個函數。

copytree:遞歸復制整個目錄樹

copytree 方法可以遞歸復制整個目錄,並返回目標目錄的路徑,函數原型如下:

def copytree(src, dst, symlinks=False, 
             ignore=None, copy_function=copy2,
             ignore_dangling_symlinks=False, 
             dirs_exist_ok=False):
    ...

參數含義如下:

  • src:表示路徑的字符串,必須是一個已存在的目錄,不能是文件;
  • dst:表示路徑的字符串,必須是一個不存在的目錄,否則報錯:FileExistsError;
  • symlinks:是否遵循符號鏈接,默認為 True。如果為 True,表示復制文件,如果為 False,那麼當 src 為軟連接時,則創建一個新的軟連接;
  • ignore:在復制的時候,用於過濾某些文件;
  • copy_function:從默認值可以看出,表示拷貝函數,這裡采用的是 copy2,會將文件的元信息也一塊拷過去;
  • ignore_dangling_symlinks:是否忽略 symlinks,如果值為 True 則忽略,值為 False,那麼當文件不存在時則產生異常。對於不支持 os.symlink() 的平臺,此參數無任何影響;

舉個例子:

import shutil

# 將 dir1 拷貝為 dir2
shutil.copytree("dir1", "dir2")

# 將 dir1 拷貝為 dir3,同時忽略掉 .txt 結尾的文件
shutil.copytree("dir1", "dir3", 
                ignore=shutil.ignore_patterns("*.txt"))

disk_usage:獲取磁盤的使用情況

該函數接收一個參數 path,會自動獲取該路徑所在磁盤的使用情況:總空間、已使用空間和空閑空間,以字節為單位。

import shutil

disk = shutil.disk_usage("/")
print(disk)
"""
usage(total=494384795648, used=71737876480, free=422646919168)
"""
print(disk.total / 1024 / 1024 / 1024)
print(disk.used / 1024 / 1024 / 1024)
print(disk.free / 1024 / 1024 / 1024)
"""
460.4317207336426
66.81110382080078
393.6206169128418
"""

關於獲取磁盤信息,之前還介紹過一個模塊叫 psutil。

get_archive_formats:獲取支持的壓縮格式

一會要介紹文件壓縮,所以先來看看都支持哪些壓縮格式。

import shutil
from pprint import pprint

pprint(shutil.get_archive_formats())
"""
[('bztar', "bzip2'ed tar-file"),
 ('gztar', "gzip'ed tar-file"),
 ('tar', 'uncompressed tar file'),
 ('xztar', "xz'ed tar-file"),
 ('zip', 'ZIP file')]
"""

既然有壓縮,那麼就有解壓縮,get_unpack_formats 函數可以返回當前系統支持的解壓縮格式列表:

import shutil
from pprint import pprint

pprint(shutil.get_unpack_formats())
"""
[('bztar', ['.tar.bz2', '.tbz2'], "bzip2'ed tar-file"),
 ('gztar', ['.tar.gz', '.tgz'], "gzip'ed tar-file"),
 ('tar', ['.tar'], 'uncompressed tar file'),
 ('xztar', ['.tar.xz', '.txz'], "xz'ed tar-file"),
 ('zip', ['.zip'], 'ZIP file')]
"""

get_terminal_size:獲取終端窗口的大小

get_terminal_size 函數可以獲取終端窗口的大小。

import shutil

print(shutil.get_terminal_size())
"""
os.terminal_size(columns=80, lines=24)
"""

系統如果不支持查詢,或者未連接到終端,那麼默認返回 80, 24。

make_archive:創建壓縮文件

通過 make_archive 可以創建壓縮文件,函數原型如下:

def make_archive(base_name, format, root_dir=None, 
                 base_dir=None, verbose=0, dry_run=0, 
                 owner=None, group=None, logger=None):
    ...

參數含義如下:

  • base_name:表示生成的壓縮文件的名稱(不包含擴展名),也可以是完整路徑。如果隻寫文件名則保存到當前目錄,否則保存到指定路徑;
  • format:表示壓縮包格式,如 zip、tar、bztar、gztar 等,會根據 format 生成擴展名並拼接到 base_name 後面;
  • root_dir:表示要壓縮的目錄路徑,默認是當前目錄;
  • base_dir:表示要壓縮的目錄路徑,默認為當前目錄;那麼問題來瞭,它和 root_dir 之間有什麼區別呢?假設我們要對 dir1 目錄進行壓縮,壓縮後的文件名是 xx.zip。如果指定的是 root_dir="dir1",那麼 xx.zip 解壓之後得到的目錄的名字為 xx;如果指定的是 base_dir="dir1",那麼 xx.zip 解壓之後得到的目錄的名字仍是 dir1。當然不管目錄名是 xx 還是 dir1,裡面存儲的內容不變,這兩個參數我們指定一個即可;
  • verbose:已棄用;
  • dry_run:表示是否創建存檔,如果 dry_run 為 True,則不會創建存檔,但會將執行的操作記錄到 logger;
  • owner:可選參數,用於指定用戶,默認為當前用戶;
  • group:可選參數,用於指定組,默認為當前組;
  • logger:用於記錄日志,通常為 logging.Logger 對象;

make_archive 函數依賴於 zipfile 和 tarfile 模塊。

import shutil

shutil.make_archive("xx", "zip",  root_dir="dir1")

之後會在當前目錄中出現一個 xx.zip,目錄 "dir1" 裡面的所有內容都會被壓縮到裡面。

有壓縮,那麼自然有解壓縮:

shutil.unpack_archive(filename, extract_dir=None, format=None)
"""
filename: 解壓縮文件的路徑
extract_dir: 解壓到哪個目錄,未指定則解壓到當前目錄
format: 壓縮文件的格式,如:zip、bztar、gztar 等等
        如果沒有提供,那麼根據壓縮文件的擴展名進行推斷
"""

該方法同樣依賴於 zipfile 和 tarfile 兩個模塊。

move:移動文件和目錄

move 函數用於將文件或目錄移動到目標目錄,如果移動到瞭不同的文件系統中,那麼移動將會變成復制。這裡我們考慮同一個文件系統即可,想拷貝的話,建議使用 copy 函數。下面看一下 move 函數的用法:

import shutil

"""
src: 源文件或目錄
dst: 路徑不存在相當於重命名,存在則進行移動
copy_function:默認是 copy2
"""
# dir22 不存在,所以相當於將 dir2 重命名為 dir22
shutil.move("dir2", "dir22")

# dir3 存在,所以會將 dir22 移動到 dir3 中
shutil.move("dir22", "dir3")

當 dst 不存在時,無論 src 是文件還是目錄,都相當於重命名。如果 dst 存在並且是目錄,那麼 src 無論是文件還是目錄,都會被移動到 dst 裡面去。如果 dst 存在並且是文件,那麼 src 必須也是一個文件,此時相當於覆蓋,可以理解為先刪除 dst、再將 src 重命名為 dst。

rmtree:刪除整個目錄樹

rmtree 函數用於刪除整個目錄樹,參數如下:

  • path:表示路徑的字符串,必須是一個目錄,不能是文件;
  • ignore_errors:默認為 False,表示是否忽略刪除中出現的錯誤。如果為 True 表示忽略、為 False 表示不忽略;
  • onerror:一個錯誤處理函數,出現異常時自動調用,並且會往裡面傳遞三個參數:os.lstat、path(路徑)、excinfo(返回的異常信息)。如果 onerror 被省略,那麼當發生錯誤時會給出提示;
import os
import shutil

print(os.access("dir3", os.F_OK))  # True
shutil.rmtree("dir3")
print(os.access("dir3", os.F_OK))  # False

which:獲取可執行文件的路徑

我們在終端中輸入 python 的時候會自動進入交互式解釋器,這是因為在環境變量中配置瞭 python 解釋器的路徑,而通過 which 函數可以獲取相應的路徑。該函數接收的參數如下:

  • cmd:相關命令;
  • mode:用於指定需要傳遞的權限掩碼,默認為 os.F_OK | os.X_OK,表示測試路徑是否存在、並且是否可執行;
  • path:默認為 None,表示查找 cmd 命令的路徑。如果不指定則在環境變量中查找,指定瞭則在指定的路徑參數中查找。但是註意:不管該參數有沒有指定,當前目錄始終會被添加到搜索路徑中;
import shutil

print(shutil.which("python"))  # /usr/bin/python
print(shutil.which("gcc"))  # /usr/bin/gcc
print(shutil.which("xxxxx"))  # None

如果找不到的話,返回 None。

到此這篇關於Python利用shutil實現拷貝文件功能的文章就介紹到這瞭,更多相關Python shutil拷貝文件內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: