首页 > 解决方案 > 是否有用于收集超过 260 个字符的时间戳的 robocopy 开关?

问题描述

我正在尝试从 Windows 目录中的文件中提取上次修改的日期。这是我的基本脚本:

Function Get-FolderItem {
    

    [cmdletbinding(DefaultParameterSetName='Filter')]
    Param (
        [parameter(Position=0,ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True)]
        [Alias('FullName')]
        [string[]]$Path = $PWD,
        [parameter(ParameterSetName='Filter')]
        [string[]]$Filter = '*.*',    
        [parameter(ParameterSetName='Exclude')]
        [string[]]$ExcludeFile,              
        [parameter()]
        [int]$MaxAge,
        [parameter()]
        [int]$MinAge
    )
    Begin {
        $params = New-Object System.Collections.Arraylist
        $params.AddRange(@("/L","/E","/NJH","/NDL","/BYTES","/FP","/NC","/XJ","/R:0","/W:0","T:W","/TS","/UNILOG:c:\temp\test.txt"))
        #params.AddRange(@("/L","/S","/NJH","/BYTES","/FP","/NC","/NDL","/TS","/XJ","/R:0","/W:0"))
        If ($PSBoundParameters['MaxAge']) {
            $params.Add("/MaxAge:$MaxAge") | Out-Null
        }
        If ($PSBoundParameters['MinAge']) {
            $params.Add("/MinAge:$MinAge") | Out-Null
        }
    }
    Process {
        ForEach ($item in $Path) {
            Try {
                $item = (Resolve-Path -LiteralPath $item -ErrorAction Stop).ProviderPath
                If (-Not (Test-Path -LiteralPath $item -Type Container -ErrorAction Stop)) {
                    Write-Warning ("{0} is not a directory and will be skipped" -f $item)
                    Return
                }
                If ($PSBoundParameters['ExcludeFile']) {
                    $Script = "robocopy `"$item`" NULL $Filter $params /XF $($ExcludeFile  -join ',')"
                } Else {
                    $Script = "robocopy `"$item`" NULL $Filter $params"
                }
                Write-Verbose ("Scanning {0}" -f $item)
                Invoke-Expression $Script | Out-Null
                get-content "c:\temp\test.txt" | ForEach {
                    Try {
                        If ($_.Trim() -match "^(?<Children>\d+)\s(?<FullName>.*)") {
                           $object = New-Object PSObject -Property @{
                                FullName = $matches.FullName
                                Extension = $matches.fullname -replace '.*\.(.*)','$1'
                                FullPathLength = [int] $matches.FullName.Length
                                FileHash = Get-FileHash -LiteralPath "\\?\$($matches.FullName)" |Select -Expand Hash
                                Created = ([System.IO.FileInfo] $matches.FullName).creationtime
                                LastWriteTime = ([System.IO.FileInfo] $matches.FullName).LastWriteTime
                                Characters = (Get-Content -LiteralPath "\\?\$($matches.FullName)" | Measure-Object -ignorewhitespace -Character).Characters
                                Owner = (Get-ACL $matches.Fullname).Owner
                                
                            } 
                            $object.pstypenames.insert(0,'System.IO.RobocopyDirectoryInfo')
                            Write-Output $object
                        } Else {
                            Write-Verbose ("Not matched: {0}" -f $_)
                        }
                    } Catch {
                        Write-Warning ("{0}" -f $_.Exception.Message)
                        Return
                    }
                }
            } Catch {
                Write-Warning ("{0}" -f $_.Exception.Message)
                Return
            }
        }
    }
}

 $a = Get-FolderItem "C:\TargetDirectory\Folder" | Export-Csv -Path C:\Temp\output.csv -Encoding Unicode

该脚本提取少于 260 个字符的文件路径的最后修改日期。对于长度超过 260 个字符的文件,它将返回1600-12-31 4:00:00 PM的无意义日期。这是不起作用的行:

LastWriteTime = ([System.IO.FileInfo] $matches.FullName).LastWriteTime

我第一次尝试解决这个问题是找到一个以Get-开头的命令,因为这样的命令在提取文件哈希、文件路径、字符数和超过 260 个字符的文件的所有者名称时很有用。例如:

Get-Date但是似乎是关于获取当前日期。

在我的第二次尝试中,我回到了 Boe Prox 关于这个脚本的原始博客文章,并注意到他的脚本有两个我的脚本中缺少的组件:

我添加到我的脚本中,但是这样做会返回一个错误:WARNING: Cannot convert null to type "System.DateTime". 我重新检查了目录中的文件,它显然有一个日期。

我重新检查了Get-Date上的文档并尝试了

Date = Get-Date -Format o | ForEach-Object { $matches -replace ":", "." }

然而,这返回WARNING: Cannot convert value "2018/03/05 18:06:54 C:TargetDirectory\Folder\Temp.csv to type "System.IO.FileInfo". Error: " Illegal characters in path."

(注意在其他帖子中,人们建议更改服务器设置以允许存在超过 260 个字符的文件。这对我来说不是一个选项,因为我无权访问服务器。)

标签: windowspowershelldaterobocopy

解决方案


一旦你在路径中达到 260 个字符,你就达到了旧的Windows MAX_PATH 限制。为了解决这个问题,您必须在路径前添加\\?\.

在上面的代码中,您这样做是为了CharactersFileHash但在检索LastWriteTime. 例如,更改此路径将起作用:

Created = ([System.IO.FileInfo] "\\?\$($matches.FullName)").creationtime
LastWriteTime = ([System.IO.FileInfo] "\\?\$($matches.FullName)").LastWriteTime

另一种方法是使用Get-ChildItemcmdlet 以及\\?\附加到路径来检索您想要的大部分字段,而无需多次查询:

get-content "c:\temp\test.txt" | ForEach {
    Try {
        If ($_.Trim() -match "^(?<Children>\d+)\s(?<FullName>.*)") {
        
            $file = Get-ChildItem "\\?\$($matches.FullName)"
        
            $object = New-Object PSObject -Property @{
                FullName = $file.FullName
                Extension = $file.Extension
                FullPathLength = $file.FullName.Length
                FileHash = Get-FileHash -LiteralPath "\\?\$($matches.FullName)" |Select -Expand Hash
                Created = $file.CreationTime
                LastWriteTime = $file.LastWriteTime
                Characters = (Get-Content -LiteralPath "\\?\$($matches.FullName)" | Measure-Object -ignorewhitespace -Character).Characters
                Owner = (Get-ACL $matches.Fullname).Owner                                
            }
            $object.pstypenames.insert(0,'System.IO.RobocopyDirectoryInfo')
            Write-Output $object
        } Else {
            Write-Verbose ("Not matched: {0}" -f $_)
        }
    } Catch {
        Write-Warning ("{0}" -f $_.Exception.Message)
        Return
    }
}

推荐阅读