Why don't .NET objects in PowerShell use the current directory?
Asked Answered
T

6

30

When you use a .NET object from PowerShell, and it takes a filename, it always seems to be relative to C:\Windows\System32.

For example:

[IO.File]::WriteAllText('hello.txt', 'Hello World')

...will write C:\Windows\System32\hello.txt, rather than C:\Current\Directory\hello.txt

Why does PowerShell do this? Can this behaviour be changed? If it can't be changed, how do I work around it?

I've tried Resolve-Path, but that only works with files that already exist, and it's far too verbose to be doing all the time.

Trula answered 28/6, 2012 at 13:45 Comment(0)
H
22

The reasons PowerShell doesn't keep the .NET notion of current working directory in sync with PowerShell's notion of the working dir are:

  1. PowerShell working dirs can be in a provider that isn't even file system based e.g. HKLM:\Software
  2. A single PowerShell process can have multiple runspaces. Each runspace can be cd`d into a different file system location. However the .NET/process "working directory" is essentially a global for the process and wouldn't work for a scenario where there can be multiple working dirs (one per runspace).
Helianthus answered 28/6, 2012 at 15:41 Comment(2)
Fair enough. Can the normal PowerShell process have multiple runspaces?Trula
@RogerLipscombe It's not very common AFAICT for PowerShell.exe to use multiple runspaces but the PowerShell engine provides the capability. Other hosts like PowerShell_ISE and PowerGUI take advantage of multiple runspaces within a single process. Fire up ISE, press Ctrl+T (new powershell tab) and run $pid followed by $ExecutionContext.Host.Runspace.InstanceId in each tab. Notice the process id is the same but the runspace id is different.Helianthus
G
33

You can change .net working dir to powershell working dir:
[Environment]::CurrentDirectory = (Get-Location -PSProvider FileSystem).ProviderPath
After this line all .net methods like [io.path]::GetFullPath and [IO.File]::WriteAllText will work without problems

Guillen answered 30/4, 2013 at 16:59 Comment(1)
~~Why is this necessary? Why isn't [System.IO.Directory]::GetCurrentDirectory() synchronized to $PWD by default?~~ https://mcmap.net/q/11270/-why-don-39-t-net-objects-in-powershell-use-the-current-directoryMetaphosphate
H
22

The reasons PowerShell doesn't keep the .NET notion of current working directory in sync with PowerShell's notion of the working dir are:

  1. PowerShell working dirs can be in a provider that isn't even file system based e.g. HKLM:\Software
  2. A single PowerShell process can have multiple runspaces. Each runspace can be cd`d into a different file system location. However the .NET/process "working directory" is essentially a global for the process and wouldn't work for a scenario where there can be multiple working dirs (one per runspace).
Helianthus answered 28/6, 2012 at 15:41 Comment(2)
Fair enough. Can the normal PowerShell process have multiple runspaces?Trula
@RogerLipscombe It's not very common AFAICT for PowerShell.exe to use multiple runspaces but the PowerShell engine provides the capability. Other hosts like PowerShell_ISE and PowerGUI take advantage of multiple runspaces within a single process. Fire up ISE, press Ctrl+T (new powershell tab) and run $pid followed by $ExecutionContext.Host.Runspace.InstanceId in each tab. Notice the process id is the same but the runspace id is different.Helianthus
T
7

For convenience, I added the following to my prompt function, so that it runs whenever a command finishes:

# Make .NET's current directory follow PowerShell's
# current directory, if possible.
if ($PWD.Provider.Name -eq 'FileSystem') {
    [System.IO.Directory]::SetCurrentDirectory($PWD)
}

This is not necessarily a great idea, because it means that some scripts (that assume that the Win32 working directory tracks the PowerShell working directory) will work on my machine, but not necessarily on others.

Trula answered 14/5, 2018 at 19:20 Comment(2)
A link to a complete prompt function with the fix would be nice. Gist?Bougainville
github.com/rlipscombe/WindowsPowerShell/blob/master/…Trula
A
4

When you use filenames in .Net methods, the best practice is to use fully-qualified path names. Or use

$pwd\foo.cer

If you do in powershell console from:

C:\> [Environment]::CurrentDirectory

C:\WINDOWS\system32\WindowsPowerShell\v1.0

you can see what folder .net use.

Aldosterone answered 28/6, 2012 at 13:56 Comment(1)
Fair enough in scripts. Gets old real quick on the command line.Trula
S
2

That's probably because PowerShell is running in System32. When you cd to a directory in PowerShell, it doesn't actually change the working directory of powershell.exe.

See:

PowerTip article on syncing the two directories

Channel9 forum thread

Space answered 28/6, 2012 at 13:50 Comment(2)
Turns out that this is a duplicate of #4072275, but because I'm running elevated, it's not $HOME.Trula
Yep, that would do it - though if you weren't elevated and you did cd $folder you would be operating on $HOME. But basically that's why it wasn't respecting $PWD - because that code was effectively asking Windows for the process working directory, but the $PWD variable is a PowerShell artifact, not a Windows one.Space
S
1

I ran into the same problem a long time ago and now I add the following to the beginning of my profile:

# Setup user environment when running session under alternate credentials and
# logged in as a normal user.
if ((Get-PSProvider FileSystem).Home -eq "")
{
    Set-Variable HOME $env:USERPROFILE -Force
    $env:HOMEDRIVE = Split-Path $HOME -Qualifier
    $env:HOMEPATH = Split-Path $HOME -NoQualifier
    (Get-PSProvider FileSystem).Home = $HOME
    Set-Location $HOME
}
Survival answered 28/6, 2012 at 14:50 Comment(1)
That answers why it's System32, in my comment, thanks; but it doesn't directly answer the question.Trula

© 2022 - 2024 — McMap. All rights reserved.