PowerShell 创建一个包含零大小文件的重复文件夹

PowerShell create a duplicate folder with zero-size files

我想创建一组文件夹的 0 文件大小的镜像,但是虽然 robocopy 非常好,但它并没有保存我想要的所有信息:

robocopy D:\documents E:\backups\documents_$(Get-Date -format "yyyyMMdd_HHmm")\ /mir /create

/create 开关使重复文件夹中的每个文件都具有零大小,这很好,但我希望重复文件夹中的每个文件都将 [size] 附加到末尾大小以 KB 或 MB 或 GB 为单位的名称,以及每个文件的创建/上次修改时间以与原始文件完全匹配。这样,我将拥有可存档文件夹的零大小副本,但其中包含该目录中文件的所有相关信息,显示每个文件的大小以及确切的创建/上次修改时间。

是否有好的/简单的方法来遍历 PowerShell 中的树,并为每个项目创建一个包含所有相关信息的零大小文件?

这将是使用我在评论中提到的方法实现复制命令的一种方法。这应该会给你一些灵感。我不打算像我那样花那么多时间在上面,但是我 运行 它在几个目录上发现了一些问题并调试了我遇到的每个问题。这是一个非常可靠的例子。

function Copy-FolderZeroSizeFiles {
    [CmdletBinding()]
    param( [Parameter(Mandatory)] [string] $FolderPath, 
           [Parameter(Mandatory)] [string] $DestinationPath )

    $dest = New-Item $DestinationPath -Type Directory -Force

    Push-Location -LiteralPath $FolderPath
    try {
        foreach ($item in Get-ChildItem '.' -Recurse) {
                
            $relPath = Resolve-Path -LiteralPath $item -Relative

            $type = if ($item.Attributes -match 'Directory')
                         { 'Directory' }
                    else { 'File'      }

            $destItem = New-Item "$dest$relPath" -Type $type -Force

            $destItem.Attributes    = $item.Attributes
            $destItem.LastWriteTime = $item.LastWriteTime
        }
    } finally {
        Pop-Location
    }
}

注意:上面的实现很简单,将目录之外的任何内容都表示为文件。这意味着符号链接等。将是没有信息的文件,它们将链接到什么。

这里有一个函数可以将字节数转换为 N.N B/K/M/G 格式。要获得更多小数位,只需将 0 添加到格式字符串的末尾即可。

function ConvertTo-FriendlySize($NumBytes) {
    switch ($NumBytes) {
        {$_ -lt 1024}       { "{0,7:0.0}B" -f ($NumBytes)             ; break }
        {$_ -lt 1048576}    { "{0,7:0.0}K" -f ($NumBytes / 1024)      ; break }
        {$_ -lt 1073741824} { "{0,7:0.0}M" -f ($NumBytes / 1048576)   ; break }
        default             { "{0,7:0.0}G" -f ($NumBytes / 1073741824); break }
    }
}

人们通常会弄错这些转换。例如,使用 1024 * 1000 来获取兆字节(将 1K 的 base10 值与 1K 的 base2 值混合)并遵循相同的逻辑来获取 GB 和 TB 是一个常见的错误。

这是我在问题中提出的其他部分,根据需要更改 $src / $dstD:\VMs 是我保留很多虚拟机的地方) .我已经设置了所有 CreationTime、LastWriteTime、LastAccessTime,以便具有 zero-size 个文件的备份位置是源的完美表示。因为我想将其用于存档目的,所以我最终将其压缩并在压缩文件名称中包含了一个 date-time 戳记。

# Copy-FolderZeroSizeFiles
$src = "D:\VMs"
$dst = "D:\VMs-Backup"

function ConvertTo-FriendlySize($NumBytes) {
    switch ($NumBytes) {
        {$_ -lt 1024}       { "{0:0.0}B" -f ($NumBytes)             ; break }   # Change {0: to {0,7: to align to 7 characters
        {$_ -lt 1048576}    { "{0:0.0}K" -f ($NumBytes / 1024)      ; break }  
        {$_ -lt 1073741824} { "{0:0.0}M" -f ($NumBytes / 1048576)   ; break }  
        default             { "{0:0.0}G" -f ($NumBytes / 1073741824); break }  
    }
}

function Copy-FolderZeroSizeFiles($FolderPath, $DestinationPath) {
    
    Push-Location $FolderPath
    if (!(Test-Path $DestinationPath)) { New-Item $DestinationPath -Type Directory }

    foreach ($item in Get-ChildItem $FolderPath -Recurse -Force) {
            
        $relPath = Resolve-Path $item.FullName -Relative

        if ($item.Attributes -match 'Directory') {
            $new = New-Item "$DestinationPath$relPath" -ItemType Directory -Force -EA Silent
        } 
        else {
                $fileBaseName = [System.IO.Path]::GetFileNameWithoutExtension($item.Name)
                $fileExt      = [System.IO.Path]::GetExtension($item.Name)
                $fileSize     = ConvertTo-FriendlySize($item.Length)
                $new = New-Item "$DestinationPath$(Split-Path $relPath)$fileBaseName ($fileSize)$fileExt" -ItemType File
        }
        "$($new.Name) : creation $($item.CreationTime), lastwrite $($item.CreationTime), lastaccess $($item.LastAccessTime)"
        $new.CreationTime = $item.CreationTime
        $new.LastWriteTime = $item.LastWriteTime
        $new.LastAccessTime = $item.LastAccessTime
        $new.Attributes = $item.Attributes   # Must this after setting creation/write/access times or get error on Read-Only files
    }
    Pop-Location
}

Copy-FolderZeroSizeFiles $src $dst

$dateTime = Get-Date -Format "yyyyMMdd_HHmm"
$zipName = "$([System.IO.Path]::GetPathRoot($dst))$([System.IO.Path]::GetFileName($dst))_$dateTime.zip"

Add-Type -AssemblyName System.IO.Compression.FileSystem
[IO.Compression.ZipFile]::CreateFromDirectory($dst, $zipName)