将数据从一个 NTFS 驱动器移动到另一个 NTFS 驱动器时保留所有时间戳

将数据从一个 NTFS 驱动器移动到另一个 NTFS 驱动器时保留所有时间戳

我想将一个包含许多子文件夹和文件的文件夹从一个 NTFS 驱动器移动到另一个 NTFS 驱动器,但我需要在新的目标驱动器上保留原始源的元数据日期和时间戳属性值。

问题:我注意到,当我完成任务的各种复制操作时,包含至少一个文件夹/文件的文件夹中的“创建日期”、“上次修改日期”等时间戳值不会保留。我还注意到,移动的空文件夹最终将自动更改其创建日期以与其他文件夹保持一致,并且在新驱动器上的空文件夹中放置某些内容时也是如此。

在此处输入图片描述

我的努力:我尝试了几乎所有通过 Google 找到的关于此主题的工具(Robocopy、Richcopy、Microsoft SyncToy、Total Commander、Free Commander... 还有很多很多...)——所有这些工具产生的结果都差不多。没有一个工具能够全面、100% 地保存被移动的源。Robocopy 和 Richcopy(以及“Commanders”)已经很接近了,但我仍然遇到问题(在所有情况下),创建日期被错误地“保存”,最后修改日期根本无法保存,等等。除此之外,我看到的唯一真正有希望的... 是 Powershell。


我的 PowerShell 之旅

我偶然发现了这个链接:

https://stackoverflow.com/questions/34951911/powershell-move-item-folder-time-stamp

...使用此脚本:

function Move-FileWithTimestamp {
[cmdletbinding()]
param(
[Parameter(Mandatory=$true,Position=0)][string]$Path,
[Parameter(Mandatory=$true,Position=1)][string]$Destination
)

$origLastAccessTime = ( Get-Item $Path ).LastAccessTime
$fileName = ( Get-Item $Path ).Name
Move-Item -Path $Path -Destination $Destination
$(Get-Item ($Destination+'\'+$fileName)).LastAccessTime = 
$origLastAccessTime
}

上面线程中的脚本与我列出的程序相比仍然没有任何不同,但至少在这里,我有一个平台,可以根据我的确切需求更改/自定义/调整某些内容。因此,我利用我在这个领域的有限知识做了我能做的事情(例如将“.LastAccessTime”更改为“.CreationTime”,将“.LastAccessTime”替换为“LastWriteTime”等),最终相对接近保留所有时间戳(我相信在某一时刻,我保留了测试文件夹中的最后修改、最后访问和最后保存)。但是,我似乎仍然无法正确保留创建日期,而我能够用其他一切完成的事情显然仅适用于单独的测试文件夹(而不是其他任何东西,例如其中的子文件夹......但这只是因为我不知道如何在主目录之外编写这些内容的脚本)。

当谈到这些事情时,我已经陷入了困境,所以想知道是否有人愿意解决这个问题。

更新:这是我现在的情况:

function Move-FileWithTimestamp {
[cmdletbinding()]
param(
[Parameter(Mandatory=$true,Position=0)][string]$Path,
[Parameter(Mandatory=$true,Position=1)][string]$Destination
)
$origCreationTime = ( Get-Item $Path ).CreationTime
$origLastWriteTime = ( Get-Item $Path ).LastWriteTime
$origLastAccessTime = ( Get-Item $Path ).CreationTime

$fileName = ( Get-Item $Path ).Name
Move-Item -Path $Path -Destination $Destination
$(Get-Item ($Destination+'\'+$fileName)).CreationTime = $origCreationTime
$(Get-Item ($Destination+'\'+$fileName)).LastWriteTime = 
$origLastWriteTime
$(Get-Item ($Destination+'\'+$fileName)).LastAccessTime = 
$origLastAccessTime
}

这似乎保留了被移动的主文件夹的原始创建时间(以及上次修改/写入时间),但显然上次访问时间在此过程中更改为原始创建时间(似乎当文件夹移动到新驱动器时,Windows 默认会在此过程中更改上次访问时间,并且还参考这个新的上次访问时间来创建文件夹在新位置的新创建时间(它显然根本不指原始创建时间)。即如果您尝试将新的创建时间设置为等于原始创建时间,则不会产生任何结果,因为新的上次访问时间默认会自动更改新的创建时间以使其等于它。因此,如果您强制 Windows 使新的上次访问时间等于原始创建时间,那么您最终会得到正确的创建时间,但上次访问时间不正确。

因此,现在我被困在上次访问时间不正确的问题中,但其他所有时间都是正确的。另外,我不知道如何让这个功能也适用于所有子文件夹,所以如果有人知道如何做到这一点,请告诉我。

更新:

function Move-FileWithTimestamp {
[cmdletbinding()]
param(
[Parameter(Mandatory=$true,Position=0)][string]$Path,
[Parameter(Mandatory=$true,Position=1)][string]$Destination
)
$origCreationTime = ( Get-Item $Path ).CreationTime
$origLastWriteTime = ( Get-Item $Path ).LastWriteTime
$origLastAccessTime = ( Get-Item $Path ).CreationTime
$origChildCreationTime = ( Get-ChildItem $Path ).CreationTime
$origChildLastWriteTime = ( Get-ChildItem $Path ).LastWriteTime
$origChildLastAccessTime = ( Get-ChildItem $Path ).CreationTime


$fileName = ( Get-Item $Path ).Name
Move-Item -Path $Path -Destination $Destination
$(Get-Item ($Destination+'\'+$fileName)).CreationTime = $origCreationTime
$(Get-Item ($Destination+'\'+$fileName)).LastWriteTime = $origLastWriteTime
$(Get-Item ($Destination+'\'+$fileName)).LastAccessTime = 
$origLastAccessTime
$(Get-ChildItem ($Destination+'\'+$fileName)) | ForEach-Object { 
$_.CreationTime = $origChildCreationTime }
$(Get-ChildItem ($Destination+'\'+$fileName)) | ForEach-Object { 
$_.LastWriteTime = $origChildLastWriteTime }
$(Get-ChildItem ($Destination+'\'+$fileName)) | ForEach-Object { 
$_.LastAccessTime = $origChildLastAccessTime }
}

现在,我有一个主文件夹和一个子文件夹,这些子文件夹的创建日期和上次修改日期正确(但不是上次访问日期)。我不知道如何对主文件夹中的其余子文件夹以及这些子文件夹中的任何子文件夹完成此操作。

答案1

移动目录树并保留所有时间戳属性值

因此,您的目标是确保从源位置移动到目标位置的文件和文件夹的LastWriteTimeLastAccessTimeCreationTime属性值保留在其原始源位置。

本质上就是这样……

  • 用途复制项目而不是Move-Item
  • 循环遍历源并设置时间戳然后使用的属性变量值 设置项目属性以递归方式将目标中的所有文件夹和文件的属性设置为相同的值
  • 明确地做同样的事情设置项目属性 时间戳仅为文件夹对象设置属性值循环
  • 用途除去项目然后删除原始源文件对象并清理它们
  • 用途除去项目然后删除原始源文件夹对象,只清理那些

脚本

$src = "C:\Src\Folder\123\"
$dest = "X:\Dest\Folder\321\"
$src = $src.Replace("\","\\")

$i = Get-ChildItem -Path $src -Recurse
$i | % {     ## -- All files and folders

    $apath = $_.FullName -Replace $src,""
    $cpath = $dest + $apath

    Copy-Item -Path $_.FullName -Destination $cpath -Force

    If (Test-Path $cpath)
       {
           Set-ItemProperty -Path $cpath -Name CreationTime -Value $_.CreationTime
           Set-ItemProperty -Path $cpath -Name LastWriteTime -Value $_.LastWriteTime
           Set-ItemProperty -Path $cpath -Name LastAccessTime -Value $_.LastAccessTime
       }
    }

$d = Get-ChildItem -Path $src -Recurse -Directory
$d | % {     ## -- Folders only

    $apath = $_.FullName -Replace $src,""
    $cpath = $dest + $apath

    If (Test-Path $cpath)
       {
           Set-ItemProperty -Path $cpath -Name CreationTime -Value $_.CreationTime
           Set-ItemProperty -Path $cpath -Name LastWriteTime -Value $_.LastWriteTime
           Set-ItemProperty -Path $cpath -Name LastAccessTime -Value $_.LastAccessTime
       }
    }


$f = Get-ChildItem -Path $src -Recurse -File
$f | % {     ## -- Delete files only

    $apath = $_.FullName -Replace $src,"" 
    $cpath = $dest + $apath

    If (Test-Path $cpath)
       {
            Remove-Item $_.FullName -Force -ErrorAction SilentlyContinue
       }
    }


$d | % {     ## -- Delete directories only

    $apath = $_ -Replace $src,"" 
    $cpath = $dest + $apath

    If (Test-Path $cpath)
       {
            Remove-Item $_.FullName -Recurse -Force -ErrorAction SilentlyContinue
       }
    }

更多资源

答案2

使用 NTFS,您只能移动单个磁盘上的文件和文件夹。移动单个磁盘上的目录是针对文件指针的操作,而不是磁盘上的数据位置。

NTFS 移动(单个磁盘)

您可以简单地从 Powershell 调用 .NET Move 函数。1
. 创建文件 move.ps1

2.复制以下内容并根据需要更改路径。

$Source = "C:\test"
$Destination = "C:\test1"
[System.IO.Directory]::Move($Source, $Destination)
  1. 右键单击该文件并选择在 Powershell 中运行。

如果您要从一个磁盘“移动”到另一个磁盘,则需要执行 NTFS 复制,然后删除源。NTFS 的默认行为是更新文件夹日期,因为从技术上讲,它们是新磁盘上新创建的文件夹(NTFS 指针)。要覆盖此行为,您必须按照其他人发布的格式设置文件和文件夹的新副本上的日期。这是执行相同操作的另一种方法。在这里,我从 Powershell 调用 C# 代码。

NTFS“移动”(多个磁盘)- 递归地将源复制到目标,复制属性,然后删除源。

$source = @"
using System;
using System.IO;

public class DirectoryCopyExample
{
    public static void DirectoryCopy(string sourceDirName, string destDirName)
    {
        // Get the subdirectories for the specified directory.
        DirectoryInfo dir = new DirectoryInfo(sourceDirName);

        if (!dir.Exists)
        {
            throw new DirectoryNotFoundException(
                "Source directory does not exist or could not be found: "
                + sourceDirName);
        }

        // Since we are deleting the files in the source directory, we need to save the dates before they are modified
        DateTime dirCreationTime = Directory.GetCreationTime(sourceDirName);
        DateTime dirLastAccessTime = Directory.GetLastAccessTime(sourceDirName);
        DateTime dirLastWriteTime = Directory.GetLastWriteTime(sourceDirName);

        DirectoryInfo[] dirs = dir.GetDirectories();
        // If the destination directory doesn't exist, create it.
        if (!Directory.Exists(destDirName))
        {
            Directory.CreateDirectory(destDirName);        
        }

        // Get the files in the directory and copy them to the new location.
        FileInfo[] files = dir.GetFiles();
        foreach (FileInfo file in files)
        {
            string temppath = Path.Combine(destDirName, file.Name);

            file.CopyTo(temppath, false);

            File.SetCreationTime(temppath, File.GetCreationTime(file.FullName));
            File.SetLastAccessTime(temppath, File.GetLastAccessTime(file.FullName));
            File.SetLastWriteTime(temppath, File.GetLastWriteTime(file.FullName));

            File.Delete(file.FullName);
        }

        // Recursively copy all sub directories
        foreach (DirectoryInfo subdir in dirs)
        {
            string temppath = Path.Combine(destDirName, subdir.Name);
            DirectoryCopy(subdir.FullName, temppath);
        }

        Directory.SetCreationTime(destDirName, dirCreationTime);
        Directory.SetLastAccessTime(destDirName, dirLastAccessTime);
        Directory.SetLastWriteTime(destDirName, dirLastWriteTime);

        Directory.Delete(sourceDirName);

    }
}
"@

Add-Type -TypeDefinition $source

[DirectoryCopyExample]::DirectoryCopy("C:\test", "D:\test")

代码借用自这里:

https://docs.microsoft.com/en-us/dotnet/standard/io/how-to-copy-directories

和这里

https://stackoverflow.com/questions/2143460/how-to-convert-c-sharp-code-to-a-powershell-script

免责声明:本产品仅经过最低限度的测试。使用时风险自负。

相关内容