Fors1k
Fors1k

Reputation: 520

PowerShell Core. Relative path issue

PowerShell:

$file = "text.txt"
Test-Path $file            
# True
 
Get-Location
# C:\Users\Fors1k

[IO.File]::OpenRead($file) 
# FileStream
 
[IO.FileInfo]::new($file)  
# Mode                 LastWriteTime         Length Name                         
# ----                 -------------         ------ ----                         
# -a----        05.03.2021     18:12              7 text.txt  
                      
gc $file                   
# test123

Everything is ok.

PowerShell Core:

$file = "text.txt"
Test-Path $file            
# True
 
Get-Location
# C:\Users\Fors1k

[IO.File]::OpenRead($file) 
# Exception calling "OpenRead" with "1" argument(s): "Could not find file 'C:\Windows\system32\text.txt'."
 
[IO.FileInfo]::new($file)  
# Mode                 LastWriteTime         Length Name                         
# ----                 -------------         ------ ----
# larhs          01.01.1601     3:00  
                      
gc $file                   
# test123

Here i got errors.

As i understand, problem is that the .NET methods think, that current location is sysfolder, not real current location.

Responding to recommendations:

I understand, that there are many solutions:

$file = "text.txt"
[Environment]::CurrentDirectory = Get-Location
[IO.File]::OpenRead($file)
#
#
$file = "text.txt"
[IO.Directory]::SetCurrentDirectory((Get-Location))
[IO.File]::OpenRead($file)
#
#
$file = "text.txt"
$file = Convert-Path $file
[IO.File]::OpenRead($file)
#
#
# My solution, before asking, was
$file = "text.txt"
$file = Get-Item $file
[IO.File]::OpenRead($file)

But all of this is "fix on the go"(I don't know the correct English idiom for this phrase).

I mean, why this works normally on posh 5.1?

Why this code: [IO.File]::OpenRead("text.txt") works on pwsh my friends, which ones I asked?

Maybe there is still a setting like (exaggerating):[DotNet.Path]::SetDefaultLocation("TheSameAsPwsh")

@mathias-r-jessen , got your point:

[Environment]::CurrentDirectory and Get-Location is a initially unrelated things.

PowerShell:

"$(Get-Location)" -eq ([Environment]::CurrentDirectory)
# True
##
##
Set-Location ([Environment]::GetFolderPath("desktop"))

"$(Get-Location)" -eq ([Environment]::CurrentDirectory)
# False (!!!)

So, my question with an understanding of what is happening:

Why my pwsh has [Environment]::CurrentDirectory as C:\Windows\system32, and is it possible to change this default value to user folder like it is in posh?

Upvotes: 3

Views: 2174

Answers (2)

Joma
Joma

Reputation: 3859

Change the current working directory for .NET.

[Environment]::CurrentDirectory = "$(Get-Location)"

or

[System.IO.Directory]::SetCurrentDirectory("$(Get-Location)")

Alternative code - Same result
Your code uses Streams. You should free/close the stream resources at end, with try -finally.

$file = "text.txt"


if (! (Test-Path $file -PathType Leaf)) {
    New-Item $file
}

Get-Content $file

File.OpenRead(String path) Method
Remarks
The path parameter is permitted to specify relative or absolute path information. Relative path information is interpreted as relative to the current working directory. To obtain the current working directory, see GetCurrentDirectory.

Directory.GetCurrentDirectory Method
Remarks
The current directory is distinct from the original directory, which is the one from which the process was started.

Checking this method in pwsh.exe and powershell.exe. The returned values are different. I think that each one has a different implementation. pwsh Windows PowerShell

if you run your code from script file. i.e. ./Script.ps1 You can use $PSScriptroot global variable, it contains the path of the directory where the script is running.

Write-Host "ScriptPath: $PSScriptroot"
[Environment]::CurrentDirectory = "$PSScriptroot"
Write-Host "CurrentDirectory: $([Environment]::CurrentDirectory)"

pwsh

Upvotes: 6

Mathias R. Jessen
Mathias R. Jessen

Reputation: 174445

Changing locations in PowerShell doesn't automatically update the process' working directory - which is what .NET uses to resolve relative paths internally.

As Joma's answer excellently suggests, you can solve this by updating [Environment]::CurrentDirectory before invoking the method.

If you find yourself running into this issue often when using PowerShell interactively, add a mechanism for updating it to your prompt (the following example is lifted straight out of one of my own profile scripts) - this will keep the two "in sync":

$function:prompt = {
  if($PWD.Provider -eq 'FileSystem'){
    [Environment]::CurrentDirectory = $PWD.Path
  }

  "PS $($executionContext.SessionState.Path.CurrentLocation -replace [regex]::Escape($HOME),'~')$('>' * ($nestedPromptLevel + 1)) "
}

Upvotes: 2

Related Questions