如何让 Get-ChildItem 处理不间断的路径 space

How to get Get-ChildItem to handle path with non-breaking space

我有以下适用于大多数文件的代码。输入文件 (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,它仍然无法处理文件名中包含不间断 space 的文件。

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

我知道我的输入文件在路径中有不间断的 space,因为我可以在记事本中打开它,复制有问题的路径,粘贴到 Word 中,然后打开段落标记。它显示了一个正常的 space,然后是 2018 年之前的 NBSP。

PowerShell 没有读入 NBSP 吗?我把它错误地传递给了-LiteralPath吗?我已经无计可施了。我看到了 ,但在那种情况下,他们在脚本中以文字形式提供路径,所以我看不出如何使用该方法。

我也尝试过:-Encoding UTF8 Get-Content 参数,但没有区别。

我什至不确定如何检查代码中的 $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 说该文件不存在。

更多我尝试过的:

我在 Windows 7 机器上,Powershell 2。

再次感谢大家的回复!

Get-ChildItem 用于列出 children 所以你会给它一个目录,但看起来你给它一个文件,所以当它说它找不到路径,这是因为它找不到具有该名称的目录

相反,您可能希望使用 Get-Item -LiteralPath 来获取每个单独的项目(这与您在其父项上 运行 Get-ChildItem 时获得的项目相同。

我认为换入 Get-Item 会使您的代码按原样工作。

经过测试,我认为以上内容实际上是错误的,对此深表歉意,但我会保留以下内容以防它有帮助,即使它可能无法解决您眼前的问题。


但让我们看一下如何使用管道对其进行简化。

首先,您从一个空数组开始,然后调用可能已经 returns 一个数组的命令 (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 有一个名为 Select-Object 的漂亮 cmdlet,它接受一个输入对象和 returns 一个仅包含您想要的属性的新对象;这将使语法更清晰:

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,因此您的特殊字符仍然是安全的。

尚不清楚为什么 Sandra 的代码不起作用:PowerShell v2+ 能够检索路径包含非 ASCII 字符的文件;也许涉及具有不同字符编码的非 NTFS 文件系统?

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

$objFile = Get-ChildItem -Path ($inFile -replace ([char] 0xa0), '?')
  • 想法是替换不间断的 space 字符。 (Unicode U+00A0;hex.0xa)在输入文件路径中带有通配符?,表示任意单个字符.

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

  • 假设,基于通配符的路径可以匹配 多个 文件;如果是这种情况,则必须检查各个匹配项以确定在 ?.

    [=41= 的位置具有不间断 space 的特定匹配项]