Home > Enterprise >  powershell Get-Content and .Net Open() handing path differently
powershell Get-Content and .Net Open() handing path differently

Time:12-07

Get-Content appears to use the current working directory location to resolve realative paths. However, the .Net System.Io.File Open() method does not. What is the PowerShell-centric way to resolve a relative path for .Net?

PS C:\src\t> type .\ReadWays.ps1
[CmdletBinding()]
param (
    [Parameter(Mandatory=$true)]
    [String]$Path
)
Write-Host "Path is $Path"
Get-Content -Path $Path | Out-Null
if ([System.IO.StreamReader]$sr = [System.IO.File]::Open($Path, [System.IO.FileMode]::Open)) { $sr.Close() }

PS C:\src\t> .\ReadWays.ps1 -Path '.\t.txt'
Path is .\t.txt
MethodInvocationException: C:\src\t\ReadWays.ps1:8
Line |
   8 |  if ([System.IO.StreamReader]$sr = [System.IO.File]::Open($Path, [Syst …
     |      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     | Exception calling "Open" with "2" argument(s): "Could not find file 'C:\Program Files\PowerShell\7\t.txt'."

PS C:\src\t> $PSVersionTable.PSVersion.ToString()
7.2.0

CodePudding user response:

I would personally type constraint the Path parameter to System.IO.FileInfo then you can access the FullName property to handle relative paths:

param(
    [ValidateScript({ 
        if(Test-Path $_ -PathType Leaf)
        {
            return $true
        }
        throw 'Invalid File Path'
    })]
    [System.IO.FileInfo]$Path
)

$reader = [System.IO.StreamReader]::new(
    [System.IO.File]::Open(
        $Path.FullName, [System.IO.FileMode]::Open
    )
)

$reader.BaseStream
$reader.Close()
PS /> ./script.ps1 ./test.txt

Handle         : 244
CanRead        : True
CanWrite       : True
SafeFileHandle : Microsoft.Win32.SafeHandles.SafeFileHandle
Name           : /home/user/Documents/test.txt
IsAsync        : False
Length         : 381
Position       : 0
CanSeek        : True
CanTimeout     : False
ReadTimeout    : 
WriteTimeout   : 

Above worked fine for me on Linux however, on Windows seem to behave differently, thanks to OP for pointing this out. To put it into perspective:

  • On Linux
PS /home/user/Documents> ([System.IO.FileInfo]'./test.txt').FullName
/home/user/Documents/test.txt
  • On Windows
PS C:\Users\User\Documents> ([System.IO.FileInfo]'.\test.txt').FullName
C:\Users\User\test.txt

Clearly, [System.IO.FileInfo] can't handle relative paths as I expected, hence I have updated the code which works for me on Windows and Linux:

param(
    [ValidateScript({ 
        if(Test-Path $_ -PathType Leaf)
        {
            return $true
        }
        throw 'Invalid File Path'
    })]
    [string]$Path
)

if(-not(Split-Path $Path -IsAbsolute))
{
    [string]$Path = Resolve-Path $Path
}

$reader = [System.IO.StreamReader]::new(
    [System.IO.File]::Open(
        $Path, [System.IO.FileMode]::Open
    )
)

$reader.BaseStream
$reader.Close()

CodePudding user response:

This is a common question. Somehow .net and powershell don't agree on the current directory.

[System.IO.File]::Open("$pwd\$Path", [System.IO.FileMode]::Open)
  • Related