Shutil尝试复制不存在的目录

时间:2019-10-28 04:51:58

标签: python python-3.x directory shutil copying

我试图复制Windows用户配置文件下的Documents文件夹,shutil.copytree(documents, destination)部分起作用。它将文档复制到目的地的根目录,在本例中为R:驱动器,并且还将尝试复制其他Windows特殊文件夹(“我的音乐”,“我的图片”等),即使它们在“文档”下不存在。

import shutil


def main():

   try:

       user_profile = os.getenv("USERPROFILE")

       # Construct a full path to the documents folder 
       documents = Path(user_profile).joinpath("Documents")

       # Destination for the Documents folder
       destination = Path("R:\\Test")

       shutil.copytree(documents, destination)            

   except (FileNotFoundError, shutil.Error) as error:
       print(error)


if __name__ == '__main__':
    main()

这是抛出的异常的摘录:

[('C:\\Users\\ConsoleGeek\\Documents\\My Music', 'R:\\Test\\My Music', 
"[WinError 5] Access is denied: 'C:\\\\Users\\\\ConsoleGeek\\\\Documents\\\\My Music'")
...

“文档”下没有这些文件夹,因此我不完全理解为什么shutil试图复制这些特殊文件夹。如果我尝试在用户配置文件下复制常规文件夹,则可以使用。

2 个答案:

答案 0 :(得分:0)

我不太了解Windows系统,但是您可以尝试致电

shutil.copytree(documents, destination, symlinks=True)

如果这些“特殊文件夹”是符号链接(或Windows的等效符号链接)。

答案 1 :(得分:0)

要了解为什么copytree()在尝试复制Documents时尝试复制special folders,我们必须首先查看隐藏的文件。

使用cmd.exe:

如果我们打开cmd.exe并将目录更改为Documents,则可以运行dir /A:H /B来查看所有文件夹(包括隐藏的文件夹)。

C:\Users\ConsoleGeek\Documents>dir /A:H /B
desktop.ini
My Music
My Pictures
My Videos

使用Powershell

如果我们打开powershell.exe并键入Get-ChildItem -Force -File -Path .\Documents\,它将列出Documents文件夹中的所有项目。

注意:它不显示特殊文件夹。

PS C:\Users\ConsoleGeek> Get-ChildItem -Force -File -Path .\Documents\


    Directory: C:\Users\ConsoleGeek\Documents


Mode                LastWriteTime         Length Name
----                -------------         ------ ----
-a-hs-        10/4/2019   6:00 PM            402 desktop.ini

使用Python

我在VSCode中运行了以下代码:

def main():

    try:
        user_profile = os.getenv("USERPROFILE")
        documents = Path(user_profile).joinpath("Documents")

        for item in documents.iterdir():
            print(item)

    except (FileNotFoundError, shutil.Error) as error:
       print(error)

if __name__ == '__main__':
    main()


# Output:
C:\Users\ConsoleGeek\Documents\desktop.ini
C:\Users\ConsoleGeek\Documents\My Music
C:\Users\ConsoleGeek\Documents\My Pictures
C:\Users\ConsoleGeek\Documents\My Videos

使用文件资源管理器

如果我选择了显示隐藏文件的选项,则Documents文件夹仍然为空。

递归复制

使用cmd.exe

使用xcopy命令和/E /H开关,我们可以复制所有文件夹/文件,包括空文件夹和隐藏文件夹。

C:\Users\ConsoleGeek>xcopy Documents R:\Sync\Documents /E /H
Documents\desktop.ini
Access denied
Unable to create directory - R:\Sync\Documents\My Music
1 File(s) copied

1 File(s) copied具有误导性,因为当启用“显示隐藏的文件”时,您可以验证没有任何内容被复制。

使用Powershell

Copy-Item cmdlet与-Recurse开关一起使用,会出现访问拒绝错误,类似于cmd.exe

这是错误消息的摘录。

Copy-Item : Access to the path 'C:\Users\ConsoleGeek\Documents\My Music' is denied.

解决方案

使用cmd.exe

xcopy具有/Exclude:filename.txt参数。您为文件提供了要排除的文件夹/文件名。

My Music
My Pictures
My Video
desktop.ini

并与xcopy命令一起使用:

C:\Users\ConsoleGeek>xcopy Documents R:\Sync\Documents /E /H /EXCLUDE:filenames.txt

使用Powershell

-Exclude参数不能与-Recurse参数一起使用,因此它将仅复制空的My Music,My ...文件夹。要将它们排除在复制之外,您可以为-Exclude参数提供通配符。

Copy-Item -Path .\Documents\ -Destination "R:\Sync\Documents" -Exclude ("My*", ".ini")

如果您需要复制子目录,但要排除某些子目录,则可以构建您不想复制的目录列表,并遍历Documents文件夹,并对照该列表检查每个项目。您可能需要进行试验,以了解适合您的要求。

使用Python

根据要求,您的脚本显然会有所不同,但是您可以使用此功能来过滤特殊文件夹和.ini文件。

import os
from pathlib import Path
import shutil
from typing import Tuple
from shutil import copytree, ignore_patterns


def copy_documents_folder(source: Path, destination: Path, ext_to_ignore: Tuple):
    copytree(source, destination, ignore = ignore_patterns(*ext_to_ignore))

def main():

   try:

       user_profile = os.getenv("USERPROFILE")
       documents = Path(user_profile).joinpath("Documents")
       destination = Path("R:\\Sync\\Documents")

       copy_documents_folder(source=documents, destination=destination, 
       ext_to_ignore=("*.ini", "My *"))


   except (FileNotFoundError, shutil.Error) as error:
       print(error)


if __name__ == '__main__':
    main()