如何让Get-ChildItem处理带有不间断空格的路径

3

我有以下的代码,适用于大多数文件。输入文件(FoundLinks.csv)是一个UTF-8文件,每行一个文件路径。这些文件路径是需要处理的特定驱动器上的完整路径。

$inFiles = @()
$inFiles += @(Get-Content -Path "C:\Users\sw_admin\FoundLinks.csv")

foreach ($inFile in $inFiles) {
    Write-Host("Processing: " + $inFile)
    $objFile = Get-ChildItem -LiteralPath $inFile
    New-Object PSObject -Prop @{ 
        FullName = $objFile.FullName
        ModifyTime = $objFile.LastWriteTime
    }
} 

但是,即使我使用了-LiteralPath,它仍然无法处理文件名中包含非断空格的文件。

Processing: q:\Executive\CLC\Budget\Co  2018 Budget - TO Bob (GA Prophix).xlsx
Get-ChildItem : Cannot find path 'Q:\Executive\CLC\Budget\Co  2018 Budget - TO Bob (GA Prophix).xlsx'
because it does not exist.
At ListFilesWithModifyTime.ps1:6 char:29
+     $objFile = Get-ChildItem <<<<  -LiteralPath $inFile
    + CategoryInfo          : ObjectNotFound: (Q:\Executive\CL...A Prophix).xlsx:String) [Get-ChildItem], ItemNotFound
   Exception
    + FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetChildItemCommand

我知道我的输入文件路径中有不间断空格,因为我可以在记事本中打开它,复制有问题的路径,将其粘贴到Word中,并打开段落标记。在2018年之前它显示正常空格后跟着一个NBSP。
PowerShell读取NBSP吗?我是否将其传递给-LiteralPath时出了问题?我已经无计可施了。我看到此解决方案,但在那种情况下,他们是在脚本中提供字面路径,所以我不知道如何使用该方法。
我还尝试过:在Get-Content上使用-Encoding UTF8参数,但没有任何区别。
我甚至不确定如何在代码中检查$inFile,只是为了确认它是否仍然包含NBSP。
感谢任何帮助来解决这个问题!
确认$inFile中有NBSP
谢谢大家!根据@TheMadTechnician的建议,我已经更新了代码,并且将我的输入文件减少到只有一个文件有问题。
$inFiles = @()
$inFiles += @(Get-Content -Path "C:\Users\sw_admin\FoundLinks.csv" -Encoding UTF8)

foreach ($inFile in $inFiles) {
    Write-Host("Processing: " + $inFile)

    # list out all chars to confirm it has an NBSP
    $inFile.ToCharArray()|%{"{0} -> {1}" -f $_,[int]$_}

    $objFile = Get-ChildItem -LiteralPath $inFile
    New-Object PSObject -Prop @{ 
        FullName = $objFile.FullName
        ModifyTime = $objFile.LastWriteTime
    }
} 

现在我可以确认$inFile确实仍然包含NBSP,就像它传递给Get-ChildItem一样。然而,Get-ChildItem却说文件不存在。
我尝试过以下操作:
- 如果我使用Get-Item而不是Get-ChildItem,则结果相同。 - 如果我使用-Path而不是-LiteralPath,则结果相同。 - Windows资源管理器和Excel可以成功处理该文件。
我使用的是Windows 7机器,PowerShell版本为2。
再次感谢所有回复!

你为什么要使用-LiteralPath?我没有看到你的路径中有任何可以被解释为通配符语言的内容。 - Maximilian Burszley
你可以检查NBSP,但我建议找到其中一个带有它的名称并专门检查它。假设$inFiles [4](第5个文件)有一个NBSP。您可以运行此命令,并查找应该是NBSP的内容,然后查看旁边空格的数字是否与其相同:$inFiles [4] .ToCharArray() |%{“ {0} - > {1}” -f $ _,[int] $ _} - TheMadTechnician
1
@TheIncorrigible1:无论是为了稳健性还是概念上的清晰度,在处理文字路径时,使用“-LiteralPath”总是值得的。 - mklement0
@mklement0 我知道这一点,但我认为OP对其使用情况有错误的想法。 - Maximilian Burszley
1
@TheIncorrigible1:这个问题讨论的是包含完整路径的输入文件,并使用“-LiteralPath”来访问由这些路径标识的文件——这听起来对我来说是正确的想法。 - mklement0
3个回答

2

目前尚不清楚为什么Sandra的代码无法正常工作:PowerShell v2+ 可以检索包含非ASCII字符的路径文件;也许涉及不同字符编码的非NTFS文件系统?

但是,以下解决方法被证明是有效的:

$objFile = Get-ChildItem -Path ($inFile -replace ([char] 0xa0), '?')
  • 这个想法是用通配符字符?替换输入文件路径中的非断空格字符(Unicode U+00A0; hex. 0xa),该通配符表示任何单个字符

  • 为了使Get-ChildItem执行通配符匹配,必须使用-Path而不是-LiteralPath(请注意,如果您在位置上传递路径参数作为第一个参数,则-Path实际上是默认值)。

  • 假设基于通配符的路径可以匹配多个文件;如果是这种情况,则必须检查各个匹配项以识别具有非断空格的?位置的特定匹配项。


2
谢谢!我之前应该说过,Q:驱动器是DFS路径。问题文件夹位于NetApp设备上,该设备呈现为Windows文件服务器。但我仍然没有看到根本原因,因为我首先获取Excel文件列表的方式是使用cmd /u窗口和dir /b。然后处理所有具有链接到其他Excel文件的Excel文件,从而得到我需要PowerShell现在处理的列表。因此,命令提示符和Excel Interop能够在同一驱动器上列出/处理这些文件。大 耸肩。我很高兴问题已经解决,我可以继续前进了。 - Sandra
1
@Sandra:感谢您告诉我们;如果您有兴趣进一步探索,请尝试使用PowerShell通过枚举来正确检索文件名;例如,使用Get-ChildItem -File -Name q:\Executive\CLC\Budget > list.txt - mklement0
谢谢,@briantist。 - mklement0

1

Get-ChildItem 用于列出子项,所以你需要给它一个目录,但看起来你给它的是一个文件,所以当它说找不到路径时,是因为它找不到该名称的目录

相反,你应该使用 Get-Item -LiteralPath 来获取每个单独的项目(这将是你在其父级上运行 Get-ChildItem 后获得的相同项目)。

我认为将 Get-Item 替换进你的代码会使其正常工作。

经过测试,我发现上面的内容实际上是错误的,对此我感到抱歉,但我将保留下面的内容,以防它有所帮助,即使它可能无法解决你当前的问题。


但是让我们来看一下如何使用管道简化它。

首先,您从一个空数组开始,然后调用一个命令(Get-Content),该命令很可能已经返回了一个数组,将其包装在一个数组中,然后将其连接到空数组中。

您可以直接这样做:

$inFiles = Get-Content -Path "C:\Users\sw_admin\FoundLinks.csv"

是的,$inFiles 可能只包含一个项目而不是整个数组。

但好处在于,foreach 完全不会介意!

你可以像这样做,它就能正常工作:

foreach ($string in "a literal single string") {
    Write-Host $string
}

但是Get-Item(以及Get-ChildItem)接受管道输入,因此它们可以接受多个项。

这意味着您可以这样做:

$inFiles = Get-Content -Path "C:\Users\sw_admin\FoundLinks.csv" | Get-Item

foreach ($inFile in $inFiles) {
    Write-Host("Processing: " + $inFile)
    New-Object PSObject -Prop @{ 
        FullName = $inFile.FullName
        ModifyTime = $inFile.LastWriteTime
    }
} 

但更重要的是,有一个处理项的管道感知 cmdlet,名为 ForEach-Object,你需要传递一个 [ScriptBlock],其中 $_ 代表当前项,因此我们可以这样做:

Get-Content -Path "C:\Users\sw_admin\FoundLinks.csv" | 
    Get-Item |
    ForEach-Object -Process {
        Write-Host("Processing: " + $_)
        New-Object PSObject -Prop @{ 
            FullName = $_.FullName
            ModifyTime = $_.LastWriteTime
        }
    }

一条龙管道!

进一步地,您正在创建一个具有所需2个属性的新对象。

PowerShell有一个很棒的cmdlet叫做Select-Object,它接收一个输入对象并返回一个仅包含您想要的属性的新对象;这将使语法更加清晰:

Get-Content -Path "C:\Users\sw_admin\FoundLinks.csv" | 
    Get-Item |
    Select-Object -Property FullName,LastWriteTime

这就是管道传递真实对象的威力。
我知道最后一个示例没有将处理消息写入屏幕,但如果您想要,可以重新添加它:
Get-Content -Path "C:\Users\sw_admin\FoundLinks.csv" | 
    Get-Item |
    ForEach-Object -Process {
        Write-Host("Processing: " + $_)
        $_ | Select-Object -Property FullName,LastWriteTime
    }

但是你也可以考虑到,许多cmdlet支持详细输出,并尝试将-Verbose添加到您现有的某些cmdlet中。不幸的是,在这种情况下它并没有真正帮助。

最后一点,当您通过管道传递项目给文件系统cmdlet时,它们绑定到的参数实际上是-LiteralPath而不是-Path,所以您的特殊字符仍然安全。


1
如果他们真的关心它是一个数组,他们可以使用[string[]]$inFiles = GC 'C:\Users\sw_admin\FoundLinks.csv' -Encoding UTF8 - TheMadTechnician
@TheMadTechnician 是的,那是真的。 - briantist
1
@briantist,非常抱歉。我的问题标题不再与我的问题匹配。我最初使用了一个管道,但随着我在问题中输入更多信息,stackoverflow给出了更好的建议,我继续尝试不同的方法。我“解开”了管道,以便更清楚地了解哪一行/哪个文件存在问题。非常感谢您提供如此详细的信息,我一定会保存下来以备将来使用。 - Sandra
我现在已经尝试在 foreach 循环之前使用 Get-Item,但是结果还是一样。它仍然会在包含 NBSP 的文件上出错。 - Sandra

0

我遇到了同样的问题。看起来get-childitem或gci期望路径为unicode(UTF-16)。因此,要么将csv文件转换为unicode,要么在脚本中将包含路径的行转换为unicode。 在PS 5.1.22621.608上测试过。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接