无法读取文件内容
Posted
技术标签:
【中文标题】无法读取文件内容【英文标题】:unable to read contents of file 【发布时间】:2021-12-26 01:17:30 【问题描述】:我正在尝试读取文件的内容:
$releaseNotesPath = "$(System.DefaultWorkingDirectory)\_ccp-develop\ccp\ccp\ReleaseNotes\ReleaseNotes\"
$latestReleaseNotesFile = Get-ChildItem -Path $releaseNotesPath -Filter *.txt | Select-Object FullName,Name | Sort-Object -Property Name | Select-Object -First 1
问题出现在这里:
$releaseNote = Get-Content $latestReleaseNotesFile
2021-11-14T14:29:07.0729088Z ##[error]Cannot find drive. A drive with the name '@FullName=D' does not exist.
2021-11-14T14:29:07.1945879Z ##[error]PowerShell exited with code '1'.
我做错了什么?
【问题讨论】:
【参考方案1】:您需要提供文件路径(FullName
):
$releaseNote = Get-Content $latestReleaseNotesFile.FullName
【讨论】:
【参考方案2】:Shayki Abramczyk 已经回答了如何,我将附上 why 部分。
那么,让我们一步一步来看看发生了什么
# Assign a value to variable, simple enough
$latestReleaseNotesFile =
# Get a list of all
Get-ChildItem -Path $releaseNotesPath -Filter *.txt |
# Interested only on file full name and shortname. Here's the catch
Select-Object FullName,Name |
# Sort the results by name
Sort-Object -Property Name |
# Return the first object of collection.
Select-Object -First 1
请注意,在 catch 部分,您正在隐式创建一个新的自定义 Powershell 对象,该对象包含两个成员:一个完全限定的文件名和一个短名称。当您稍后将自定义对象传递给Get-Content
时,它不知道如何处理自定义对象。所以,因此错误。 Shayki 的答案有效,因为它明确告诉使用包含井文件全名的FullName
成员。
【讨论】:
【参考方案3】:现有答案中有很好的信息;让我总结和补充它们:
您的命令的简化和强大的重新表述:
$latestReleaseNotesFile =
Get-ChildItem -LiteralPath $releaseNotesPath -Filter *.txt |
Select-Object -First 1
$releaseNote = $latestReleaseNotesFile | Get-Content
Get-ChildItem
-LiteralPath
参数确保其参数被逐字处理,而不是作为通配符表达式,这是-Path
所期望的。
Get-ChildItem
的输出已经按名称排序(虽然这个事实没有正式记录,但它是用户开始依赖的行为,并且不会改变)。
通过不使用Select-Object FullName, Name
将Get-ChildItem
输出的System.IO.FileInfo
实例转换为仅具有指定属性的[pscustomobject]
实例,生成的对象可以作为一个整体 被传送到Get-Content
,它由其.PSPath
属性值隐式绑定到-LiteralPath
(别名为-PSPath
),其中包含完整路径(带有PowerShell provider 前缀)。
至于你尝试了什么:
Get-Content $latestReleaseNotesFile
这个位置将变量$latestReleaseNotesFile
的值绑定到Get-Content
的-Path
参数。
由于-Path
是[string[]]
类型的(即,它接受一个或多个字符串;使用Get-Help Get-Content
来查看),$latestReleaseNotesFile
的值是字符串化的 如有必要,通过其.ToString()
方法。
Select-Object FullName, Name
这将创建具有.FullName
和.Name
属性的[pscustomobject]
实例,其值取自Get-ChildItem
输出的System.IO.FileInfo
实例。
对[pscustomobject]
实例进行字符串化会产生一个非正式、哈希表-类似 的表示,仅适用于人类观察者;例如:
# -> '@FullName=/path/to/foo; Name=foo)'
"$([pscustomobject] @ FullName = '/path/to/foo'; Name = 'foo' ))"
注意:我使用 可扩展字符串 ("..."
) 进行字符串化,因为直接调用 .ToString()
会意外产生 空字符串,因为GitHub issue #6163 中描述的一个长期存在的错误。
不出所料,传递内容为 @FullName=/path/to/foo; Name=foo)
的字符串不是有效的文件系统路径,并导致您看到的错误。
如 Shayki 的回答所示,改为传递 .FullName
属性值可以解决该问题:
-LiteralPath
而不是(位置隐含的)-Path
具体来说,包含逐字的[
或]
的路径将被误解为wildcard expression。
Get-Content -LiteralPath $latestReleaseNotesFile.FullName
如顶部所示,坚持使用System.IO.FileInfo
实例并通过管道 隐式绑定到-LiteralPath
:
# Assumes that $latestReleaseNotesFile is of type [System.IO.FileInfo]
# This is the equivalent of:
# Get-Content -LiteralPath $latestReleaseNotesFile.PSPath
$latestReleaseNotesFile | Get-Content
陷阱:因此人们会期望将相同类型的对象作为参数传递会导致 same 绑定,但那是 不是正确的:
# !! NOT the same as:
# $latestReleaseNotesFile | Get-Content
# !! Instead, it is the same as:
# Get-Content -Path $latestReleaseNotesFile.ToString()
Get-Content $latestReleaseNotesFile
也就是说,参数不被它的.PSPath
属性值绑定到-LiteralPath
;相反,字符串化值绑定到-Path
。
在 PowerShell (Core) 7+ 中,这通常不是问题,因为System.IO.FileInfo
(和System.IO.DirectoryInfo
)实例始终 > 字符串化到它们的 完整 路径(.FullName
属性值) - 但是,对于包含 [
或 ]
的文字路径,它仍然会出现故障。
在 Windows PowerShell 中,此类实例在情境上 仅字符串化为文件 name (.Name
),从而导致 出现故障和微妙可能存在错误 - 请参阅this answer。
GitHub issue #6057 讨论了这种有问题的不对称。
以下是对上述内容的总结,并附有具体指导:
将文件系统路径稳健地传递给文件处理 cmdlet:
注意:以下内容不仅适用于Get-Content
,还适用于所有文件处理标准cmdlet——不幸的是Windows PowerShell中的Import-Csv
例外,由于一个错误。
作为一个论据:
明确地使用 -LiteralPath
,因为使用 -Path
(如果 没有 参数被命名,这也是隐含的)将其参数解释为 wildcard expression,特别是导致包含[
或]
的文字文件路径被误解。
# $pathString is assumed to be a string ([string])
# OK: -LiteralPath ensures interpretation as a literal path.
Get-Content -LiteralPath $pathString
# Same as:
# Get-Content -Path $pathString
# !! Path is treated as a *wildcard expression*.
# !! This will often not matter, but breaks with paths with [ or ]
Get-Content $pathString
另外,在 Windows PowerShell 中,当传递 System.IO.FileInfo
或 System.IO.DirectoryInfo
实例时,显式使用 .FullName
(文件-system-native 路径)或 .PSPath
属性(包括 PowerShell provider 前缀;路径可能基于 PowerShell-specific drive)以确保其完整路径用来;这在 PowerShell (Core) 7+ 中不再需要,其中此类实例始终 字符串化为它们的 .FullName
属性 - 请参阅 this answer。
# $fileSysInfo is assumed to be of type
# [System.IO.FileInfo] or [System.IO.DirectoryInfo].
# Required for robustness in *Windows PowerShell*, works in both editions.
Get-Content -LiteralPath $fileSysInfo.FullName
# Sufficient in *PowerShell (Core) 7+*:
Get-Content -LiteralPath $fileSysInfo
通过管道:
System.IO.FileInfo
和System.IO.DirectoryInfo
实例,例如Get-ChildItem
和Get-Item
发出的实例,可以作为一个整体传递,并通过它们的-LiteralPath
属性值稳健地绑定到-LiteralPath
- 在两个 PowerShell 版本,因此您可以在跨版本 脚本中安全地使用这种方法。
# Same as:
# Get-Content -LiteralPath $fileSysInfo.PSPath
$fileSysInfo | Get-Content
这种机制 - 在this answer 中有更详细的解释 - 依赖于与 参数名称 匹配的 属性名称,包括参数的 别名名字。因此,any 类型的输入对象具有 .LiteralPath
、.PSPath
或仅在 PowerShell (Core) 7+ 中具有 .LP
属性(-LiteralPath
参数的所有别名)受该属性值的约束。[1]
# Same as:
# Get-Content -LiteralPath C:\Windows\win.ini
[pscustomobject] @ LiteralPath = 'C:\Windows\win.ini' | Get-Content
相比之下,任何具有.Path
属性的对象都通过该属性的值绑定到支持通配符的-Path
参数。
# Same as:
# Get-Content -Path C:\Windows\win.ini
# !! Path is treated as a *wildcard expression*.
[pscustomobject] @ Path = 'C:\Windows\win.ini' | Get-ChildItem
直接字符串输入和任何其他对象的字符串化表示也绑定到-Path
。
# Same as:
# Get-Content -Path C:\Windows\win.ini
# !! Path is treated as a *wildcard expression*.
'C:\Windows\win.ini' | Get-Content
陷阱:因此,例如,通过Get-Content
将文本文件的行提供给Get-ChildItem
,也会在包含[
或]
的路径出现故障。一个简单的解决方法是将它们作为参数传递给-LiteralPath
:
Get-ChildItem -LiteralPath (Get-Content -LiteralPath Paths.txt)
[1] 该逻辑仅适用于管道输入,而不适用于按参数输入的同一参数,这是一个不幸的不对称,在GitHub issue #6057.
【讨论】:
以上是关于无法读取文件内容的主要内容,如果未能解决你的问题,请参考以下文章