【问题标题】: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'.

我做错了什么?

【问题讨论】:

    标签: powershell azure-devops azure-pipelines


    【解决方案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.

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2022-01-15
          • 2016-03-19
          • 2021-12-25
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多