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

时间:2018-06-06 23:50:42

标签: powershell unicode character-encoding filesystems get-childitem

我有以下代码适用于大多数文件。输入文件(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吗?我的智慧结束了。我看到了this solution,但在这种情况下,他们在脚本中提供了作为文字的路径,所以我无法看到我如何使用这种方法。

我还尝试了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。

再次感谢所有回复!

2 个答案:

答案 0 :(得分:2)

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

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

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

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

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

答案 1 :(得分: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
        }
    }

全部在一个管道中!

但是,您还要创建一个具有所需属性的新对象。

PowerShell有一个名为Select-Object的漂亮cmdlet,它接受一个输入对象并返回一个只包含所需属性的新对象;这将使语法更清晰:

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