我有以下代码适用于大多数文件。输入文件(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说文件不存在。
我尝试了更多:
我在Windows 7机器上,Powershell 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
,因此您的特殊字符仍然是安全的。