【问题标题】:Zero all filesize in a large directory tree (delete file content, keep files)将大型目录树中的所有文件大小归零(删除文件内容,保留文件)
【发布时间】:2017-11-14 07:22:23
【问题描述】:

如何删除大型目录树(10 GB,1K 文件)的内容(文件大小为零),但保留整个树结构、文件名、扩展名。 (如果我能保留原来的最后一次写入时间[最后一次内容修改时间],那是一个奖励)。

我已经看到了一些针对单个文件的建议,但无法弄清楚如何使这项工作适用于整个 CWD。​​p>

def deleteContent(fName):
    with open(fName, "w"):
        pass

【问题讨论】:

  • 是linux文件系统吗?
  • 很遗憾没有。我知道那里会很容易。
  • 我会使用适当的参数运行 xcopy.exe(有一个可以生成零长度文件)。
  • 您显示的方法有什么问题,与os.walk() 之类的方法一起使用?
  • get-childitem c:\temp\test\*.* -recurse | clear-content(powershell) 怎么样?什么是 LWT?什么是 CWD?

标签: python windows python-3.x powershell


【解决方案1】:

以管理员身份运行以下应将所有内容重置为空文件并保留文件的最后写入时间

gci c:\temp\test\*.* -recurse | % {    
    $LastWriteTime = $PSItem.LastWriteTime
    clear-content $PSItem;
    $PSItem.LastWriteTime = $LastWriteTime
}

【讨论】:

  • 谢谢,遇到了一些错误,但从 10 GB 中获得了 200MB。错误,----1----ClearContentUnauthorizedAccessError,Microsoft.PowerShell.Commands.ClearContentComm and -----2---- At line:4 char:5 + $PSItem.LastWriteTime = $LastWriteTime + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : NotSpecified: (:) [], SetValueInvocationException : ExceptionWhenSetting -----3----- + $PSItem.LastWriteTime = $LastWriteTime + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : NotSpecified: (:) [], SetValueInvocationException
  • 文件很可能正在使用中。如果这是您经常需要的,则应添加适当的异常处理。
【解决方案2】:

os.walk() 将所有目录作为以下元组的列表返回:

(directory, list of folders in the directory, list of files in the directory)

当我们将您的代码与os.walk() 结合时:

import os

for tuple in os.walk("top_directory"):
    files = tuple[2]
    dir = tuple[0]
    for file in files:
        with open(os.path.join(dir, file), "w"):
            pass

【讨论】:

  • 谢谢,我收到以下错误:Traceback (most recent call last): File "m:\test\zerofile.py", line 7, with open(os.path.join(dir, file), "w"): PermissionError: [Errno 13] Permission denied: 'm:\\test\\ktop.ini'
  • 能不能尝试在以管理员身份运行的cmd中运行脚本?
  • 以管理员身份完成。同样的错误消息,似乎在目录树中的第一个文件(最上面的目录的第一个文件)。
  • 可能另一个程序正在使用ktop.ini 文件。您可以将try-except 语句添加到with open(os.path.join(dir, file), "w"): pass 行。因此代码不会清除您无权访问的文件的内容。
  • 任何简单的方法来重置代码运行的整个目录的权限?
【解决方案3】:

所有好的答案,但我可以看到提供的答案还有两个挑战:

在遍历目录树时,您可能希望限制它的深度,以保护您免受非常大的目录树的影响。其次,Windows 在文件名和路径中有 256 个字符的限制(由资源管理器强制执行)。虽然此限制会产生各种操作系统错误,但有一种解决方法。

让我们从文件路径的最大长度的解决方法开始,您可以执行以下操作作为解决方法:

import os
import platform


def full_path_windows(filepath):
    """
    Filenames and paths have a default limitation of 256 characters in Windows.
    By inserting '\\\\?\\' at the start of the path it removes this limitation.

    This function inserts '\\\\?\\' at the start of the path, on Windows only
    Only if the path starts with '<driveletter>:\\' e.g 'C:\\'.

    It will also normalise the characters/case of the path.

    """
    if platform.system() == 'Windows':
        if filepath[1:3] == ':\\':
            return u'\\\\?\\' + os.path.normcase(filepath)
    return os.path.normcase(filepath)

提到了写保护,或文件正在使用,或任何其他可能导致无法写入文件的情况,这可以通过以下方式检查(无需实际写入):

import os

def write_access(filepath):
    """
    Usage:

    read_access(filepath)

    This function returns True if Write Access is obtained
    This function returns False if Write Access is not obtained
    This function returns False if the filepath does not exists

    filepath = must be an existing file
    """
    if os.path.isfile(filepath):
        return os.access(filepath, os.W_OK)
    return False

要设置最小深度或最大深度,您可以这样做:

import os


def get_all_files(rootdir, mindepth = 1, maxdepth = float('inf')):
    """
    Usage:

    get_all_files(rootdir, mindepth = 1, maxdepth = float('inf'))

    This returns a list of all files of a directory, including all files in
    subdirectories. Full paths are returned.

    WARNING: this may create a very large list if many files exists in the 
    directory and subdirectories. Make sure you set the maxdepth appropriately.

    rootdir  = existing directory to start
    mindepth = int: the level to start, 1 is start at root dir, 2 is start 
               at the sub direcories of the root dir, and-so-on-so-forth.
    maxdepth = int: the level which to report to. Example, if you only want 
               in the files of the sub directories of the root dir, 
               set mindepth = 2 and maxdepth = 2. If you only want the files
               of the root dir itself, set mindepth = 1 and maxdepth = 1
    """    
    file_paths = []
    root_depth = rootdir.rstrip(os.path.sep).count(os.path.sep) - 1
    for dirpath, dirs, files in os.walk(rootdir):
        depth = dirpath.count(os.path.sep) - root_depth
        if mindepth <= depth <= maxdepth:
            for filename in files:
                file_paths.append(os.path.join(dirpath, filename))
        elif depth > maxdepth:
            del dirs[:]  
    return file_paths

现在将上面的代码汇总到一个函数中,这应该会给你一个想法:

import os

def clear_all_files_content(rootdir, mindepth = 1, maxdepth = float('inf')):
    not_cleared = []
    root_depth = rootdir.rstrip(os.path.sep).count(os.path.sep) - 1
    for dirpath, dirs, files in os.walk(rootdir):
        depth = dirpath.count(os.path.sep) - root_depth
        if mindepth <= depth <= maxdepth:
            for filename in files:
                filename = os.path.join(dirpath, filename)
                if filename[1:3] == ':\\':
                    filename = u'\\\\?\\' + os.path.normcase(filename)            
                if (os.path.isfile(filename) and os.access(filename, os.W_OK)):
                    with open(filename, 'w'): 
                        pass
                else:
                    not_cleared.append(filename)
        elif depth > maxdepth:
            del dirs[:]  
    return not_cleared

这不会保持“最后写入时间”。

它将返回listnot_cleared,您可以检查遇到写访问问题的文件。

【讨论】:

  • dirpath 是完全合格的,如果 rootdir 是完全合格的,所以你应该以 rootdir = os.path.abspath(rootdir) 开头。这很重要,因为必须将 u"\\\\?\\" 路径标准化为仅使用反斜杠作为路径分隔符。此外,它还支持长的相对路径,这也受到 DOS 命名空间MAX_PATH(260 个字符)的限制。 UNC 路径具有相同的限制,因此还要检查它们并将初始的u"\\\\" 重写为u"\\\\?\\UNC\\"。文件名大小写在 Windows 上无关紧要,因此调用 os.path.normcase 毫无意义。
  • 在 Windows 上,os.access 只检查只读属性——不检查安全或 I/O 共享,因此除了只读文件之外,它无助于避免 PermissionError 异常(即ERROR_ACCESS_DENIEDERROR_SHARING_VIOLATION)。只需尝试在 try/except 块中打开文件,然后忽略 PermissionError
  • @eryksun,我真的认为 os.access 正在测试访问,包括 GUI/GID,因此 PermissionError ERROR_ACCESS_DENIED 应该为 os.access 返回 False。无论如何,我会对此进行测试并在有时间时调整我的答案。感谢您提供有关 UNC 路径的提示,我会将其纳入我的日常工作中。
  • os.access 只是在 Windows 上调用 GetFileAttributes。增强它的字面意思是通过GetSecurityInfoAccessCheck。但这不会检查共享的读取、写入和删除访问权限,或父目录授予的权限(例如读取文件属性或删除访问权限)。更好的方法是尝试通过NtOpenFile 使用请求的访问权限打开句柄(本机NT I/O 不需要备份语义来打开目录,并且允许支持dir_fd),这让文件系统检查安全和共享。对于边缘情况,还有更多内容。
  • 您可以通过stat 获取每个文件的最后访问和修改时间(st_atimest_mtime),可以在通过os.utime(filename, (atime, mtime)) 截断后将其设置回来。在 Python 3 中,您可以修改遍历树以直接使用 os.scandir 而不是 os.walk。这保留了来自 WinAPI FindFirstFileFindNextFile 的基本统计信息,从而节省了在每个文件上调用 stat 的成本。
猜你喜欢
  • 2012-04-12
  • 1970-01-01
  • 1970-01-01
  • 2010-11-17
  • 2019-02-13
  • 1970-01-01
  • 1970-01-01
  • 2023-03-04
  • 1970-01-01
相关资源
最近更新 更多