有没有办法在 robocopy 脚本中包含带有变音符号的文件路径?

Posted

技术标签:

【中文标题】有没有办法在 robocopy 脚本中包含带有变音符号的文件路径?【英文标题】:Is there a way to include filepaths with diacritics in a robocopy script? 【发布时间】:2021-09-30 23:47:15 【问题描述】:

我有一个脚本,可以从目录中的每个文件中提取元数据。当文件路径没有变音符号时,脚本会生成一个如下所示的 csv 文件:

当文件路径包含变音符号(即“TéstMé.txt”)时,csv 文件的 filehash 字段中有空格:

我的问题是:无论文件路径中的变音符号如何,我如何让这个脚本正常工作?

我已确定问题不在于脚本的 Get-FileHash 部分(当我运行单行 Get-FileHash "C:\Temp\New\TéstMé.txt" 时,会产生哈希。) 我还确定将FileHash = Get-FileHash -Path 替换为FileHash = Get-FileHash -LiteralPath 不是解决方案,因为它也会产生空白。 我尝试更改($_.Trim() -match "^(?<Children>\d+)\s+(?<FullName>.*)") 行中的正则表达式,以防它阻塞变音符号,但任何更改都会导致WARNING: parsing [unique parsing error here]. 我还尝试将 ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True$true 更改为 $false(以防管道正在更改文件路径值)但没有效果。 我认为可能 Robocopy(在脚本中使用)无法处理带有变音符号的文件,但 Robocopy C:\Temp\New C:\Temp\star 可以很好地移动文件。 我确实有一个用于识别非法字符的正则表达式(从 here 获得),但我不知道如何将其合并到脚本中。 仅供参考:我无法更改实际文件名。很想对任何带有变音符号的字母进行查找和替换,但我不接受此选项。
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","/BYTES","/FP","/NC","/XJ","/R:0","/W:0","T:W"))
            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 | 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
                                    
                                 
                                $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
                
            
        
    
    
 Get-FolderItem "C:\Temp\New" | Export-Csv -Path C:\Temp\testesting.csv


【问题讨论】:

您可以尝试将参数-Encoding 设置为UTF8 用于cmdlet Export-Csv 以保留变音符号。 好建议。试过了,但没有用(输出中没有文件哈希)。但是你给了我一个想法——也许脚本前面的命令有一个类似的-encoding 开关.. 请注意结果中的完整路径:C:\Temp\New\T?stM?.txt。这意味着问题显然在您的regex 部分。我想解决办法是Re-save your script as UTF-8 with BOM.或者尝试设置控制台编码:[Console]::OutputEncoding = [System.Text.Encoding]::UTF8 向外部程序(如 robocopy)发送字符串时,要更改的编码设置为 $OutputEncoding。您可以先尝试设置$OutputEncoding = [Text.Encoding]::UTF8 @oymonk 问题出在输出不是 unicode 的,你可以改用 log 并强制它是 unicode。 【参考方案1】:

这是一个解决方案,我使用 /UNILOG:c:\temp\test.txt 参数将 RoboCopy 输出输出到 unicode 日志,然后使用相同的代码

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","/BYTES","/FP","/NC","/XJ","/R:0","/W:0","T:W","/UNILOG:c:\temp\test.txt"))
            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
                                    
                                 
                                $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:\Temp\New" | Export-Csv -Path C:\Temp\testtete.csv -Encoding Unicode

【讨论】:

以上是关于有没有办法在 robocopy 脚本中包含带有变音符号的文件路径?的主要内容,如果未能解决你的问题,请参考以下文章

有没有办法在 emscripten 中包含自定义标题?

如何在bash shell脚本中包含文件

是否可以在 shell 脚本中的多行命令中包含内联注释? [复制]

自动化测试脚本中包含adb命令,jenkins构建时找不到adb解决办法

脚本变量中包含空格的解决办法

有没有办法在 Android 中检测德国元音变音