question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Introduce a PowerShell-aware path-information class for convenient .NET interoperability

See original GitHub issue

Best practice answers and documentation often refer to .Net methods for file streaming where it concerns performance, e.g. Processing Large Files. But the .Net implementation differs from native PowerShell behavior were it concerns casting expectations and the current directory:

([io.fileinfo]'.\Test.txt').DirectoryName
C:\WINDOWS\system32
(Get-Item '.\Test.txt').DirectoryName
C:\Users\User\MyScripts

Btw. There is currently no accelerator or initiator for files:

[fileinfo]'.\Test.txt'

InvalidOperation: Unable to find type [fileinfo].

Note that [io.fileinfo]'.\Test.txt' strips the .\ from every member of the object which means if it is used for a parameter type, you can’t discover anymore that the path was relative (using a condition as e.g. if ($FilePath.Name.StartsWith('.\')) {...), which means that you can only use a general string type for this instead.

Using .net (streaming) methods generally require more coding therefore it often ends up in a separate function. As a (spoiled) PowerShell engineer, I would expect to be able to do just this (wishful thinking):

Function Stream-File([FileInfo]$FilePath) {
    $Stream = [System.IO.StreamReader]$FilePath
    # ...
    $Stream.Dispose()
}

Stream-File .\Test.txt

This implies at least two things:

  1. The (PowerShell) FileInfo constructor needs to accept a relative path that is than joined to the current path (Join-Path (Get-Location) '.\Test.txt') to prevent it expects the file in the C:\WINDOWS\system32 folder and to be accelerated to resolve the following error:

InvalidOperation: Unable to find type [FileInfo].

  1. The [FileInfo] class should automatically cast to other file classes along with System.IO.StreamReader which current results in the following error:

Cannot convert the “C:\Users\User\MyScripts\Test.txt” value of type “System.IO.FileInfo” to type “System.IO.StreamReader”. InvalidOperation:

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Reactions:1
  • Comments:17 (6 by maintainers)

github_iconTop GitHub Comments

4reactions
jborean93commented, Feb 9, 2021

This is really a known issue as the .NET methods use the process working directory whereas the PowerShell cmdlets rely on the Runspace location (see https://github.com/PowerShell/PowerShell/issues/3428 and linked issues in there). You can’t really keep them in sync because there’s only 1 process working dir but multiple Runspaces can exist inside a process. Maybe we just need to add some documentation (if there isn’t already) on the best ways to write cmdlets to handle this properly. In my case I usually do the following to handle both -Path and -LiteralPath when calling .NET methods

Function Get-ItemLikeFunction {
    [CmdletBinding(DefaultParameterSetName='Path')]
    param (
        [Parameter(
            Mandatory = $true,
            Position = 0,
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true,
            ParameterSetName = 'Path'
        )]
        [SupportsWildcards()]
        [ValidateNotNullOrEmpty()]
        [String[]]
        $Path,

        [Parameter(
            Mandatory = $true,
            Position = 0,
            ValueFromPipelineByPropertyName = $true,
            ParameterSetName = 'LiteralPath'
        )]
        [Alias('PSPath')]
        [ValidateNotNullOrEmpty()]
        [String[]]
        $LiteralPath
    )
    
    begin {
        # Some code here
    }

    process {
        if ($PSCmdlet.ParameterSetName -eq 'Path') {
            $allPaths = $Path | ForEach-Object -Process {
                $provider = $null
                $PSCmdlet.SessionState.Path.GetResolvedProviderPathFromPSPath($_, [ref]$provider)
            }
        }
        elseif ($PSCmdlet.ParameterSetName -eq 'LiteralPath') {
            $allPaths = $LiteralPath | ForEach-Object -Process {
                $PSCmdlet.SessionState.Path.GetUnresolvedProviderPathFromPSPath($_)
            }
        }

        foreach ($filePath in $allPaths) {
            # Each $filePath contains the absolute path resolved from the current PowerShell location
        }
    }

    end {
        # Some code here
    }
}

I’m not sure I agree with having a type accelerator for FileInfo specifically, this class is not really something I think we should encourage general use off in PowerShell. Usually the same operations that FileInfo (and DirInfo) are behind the FIleSystem provider included cmdlets like Get-Item, Set-Item, etc. Anyone wishing for these more advanced methods are probably ok with writing the full type name just like any other .NET class without an accelerator.

2reactions
rkeithhillcommented, Feb 18, 2022

The Engine WG reviewed this issue today. Calling .NET methods with relative paths is a pain point we recognize but we don’t believe there is enough value in adding another type e.g. [PSPathInfo] just for this scenario when you can use "$pwd\Test.txt". We do suggest submitting an enhancement request to PSSA for a rule that warns about sending relative paths to .NET members.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Needing to have user input for some paths and use relative ...
See: PowerShell issue Introduce a PowerShell-aware path-information class for convenient .NET interoperability #14745 .
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found