无法读取文件内容

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, NameGet-ChildItem输出的System.IO.FileInfo实例转换为仅具有指定属性的[pscustomobject]实例,生成的对象可以作为一个整体 被传送到Get-Content,它由其.PSPath 属性值隐式绑定到-LiteralPath(别名为-PSPath),其中包含完整路径(带有PowerShell provider 前缀)。

请参阅this answer,了解有关此基于管道的绑定如何工作的详细信息。

至于你尝试了什么

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.FileInfoSystem.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.FileInfoSystem.IO.DirectoryInfo 实例,例如Get-ChildItemGet-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.

【讨论】:

以上是关于无法读取文件内容的主要内容,如果未能解决你的问题,请参考以下文章

JAVA 读取你磁盘上任意一个文本文件,并输出内容

Java 如何读取目录下的文件内容

PHP读取目录下所有文件内容并显示

linux无法读取F盘的内容 权限不够

php fread()函数读取文本直到文件尾却无法结束

open() 无法读取我的文件的内容 [重复]