Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

"Actual" current directory and PowerShell current directory mismatch when using .NET BCL methods #3428

Closed
antiufo opened this issue Mar 26, 2017 · 6 comments
Labels
Issue-Discussion the issue may not have a clear classification yet. The issue may generate an RFC or may be reclassif Resolution-By Design The reported behavior is by design.

Comments

@antiufo
Copy link

antiufo commented Mar 26, 2017

When cd is used, the actual current directory of the process is not changed. This means that plain BCL methods still see the old current directory.

This creates an inconsistency with other ways of executing commands:

  • PowerShell cmdlets: $pwd is honored
  • External commands: $pwd is honored
  • .NET methods: $pwd is not honored

Steps to reproduce

PS C:\Users\Andrea> cd C:\temp
PS C:\temp> echo example1 > example1.txt
PS C:\temp> [IO.File]::WriteAllText('example2.txt', 'example2')

Expected behavior

C:\temp contains example1.txt and example2.txt.

Actual behavior

C:\temp only contains example1.txt. example2.txt is created in C:\Users\Andrea.

Environment data

> $PSVersionTable
Name                           Value
----                           -----
PSVersion                      5.1.15063.0
PSEdition                      Desktop
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0...}
BuildVersion                   10.0.15063.0
CLRVersion                     4.0.30319.42000
WSManStackVersion              3.0
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1

Btw the same happens with PowerShell v6.0.0-alpha.17.

@PetSerAl
Copy link
Contributor

If I have process with multiple Runspaces and each one have different current location. Which one $pwd should be honored by .NET methods?

@antiufo
Copy link
Author

antiufo commented Mar 26, 2017

Possible solutions I can think of:

  • Leave the behavior as it is
  • Take a process-wide lock when BCL methods are directly invoked (except for cmdlets), if multiple runspaces exist (I don't know to what extent it would impact the performance)
  • Switch the current directory to a dummy, default directory whenever the number of runspaces > 1 (to make the mistake clear)
  • Only honor the $pwd of the main runspace
  • Provide something like using-current-directory{ [IO.File]::WriteAllText() } that acts as a process-wide lock that sets the real current directory.

@iSazonov iSazonov added the Issue-Discussion the issue may not have a clear classification yet. The issue may generate an RFC or may be reclassif label Mar 26, 2017
@PetSerAl
Copy link
Contributor

@antiufo Process-wide lock will not going to work, unless everyone agreed to use it and performance implication are not very good. Also, not every process host PowerShell just for sake of hosting PowerShell. Process may have its own work to do and unlikely be happy if you just change its working directory to dummy one. And as Runspaces can come and gone at process wish, it may be hard to say which one is main.
Besides, IMHO, there is good enough solution for this already:

PS C:\Users\PetSerAl> function ConvertTo-FileSystemPath {
>>     param($PSPath)
>>     $PSProvider = $null
>>     $Result = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($PSPath, [ref]$PSProvider, [ref]$null)
>>     if($PSProvider.ImplementingType -eq [Microsoft.PowerShell.Commands.FileSystemProvider]) {
>>         $Result
>>     } else {
>>         Write-Error 'Not filesystem provider'
>>     }
>> }
PS C:\Users\PetSerAl> New-PSDrive Win FileSystem C:\Windows | Out-Null
PS C:\Users\PetSerAl> ConvertTo-FileSystemPath qwe
C:\Users\PetSerAl\qwe
PS C:\Users\PetSerAl> cd \
PS C:\> ConvertTo-FileSystemPath asd
C:\asd
PS C:\> ConvertTo-FileSystemPath Win:\zxc
C:\Windows\zxc
PS C:\> ConvertTo-FileSystemPath HKLM:\SOFTWARE
ConvertTo-FileSystemPath : Not filesystem provider
At line:1 char:1
+ ConvertTo-FileSystemPath HKLM:\SOFTWARE
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [Write-Error], WriteErrorException
    + FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,ConvertTo-FileSystemPath

@BrucePay
Copy link
Collaborator

This behavior is by design and it's been this way since version 1.0. We considered options like those proposed above but none of them were viewed as satisfactory for various reasons. Acquiring a global lock, then setting the process pwd, then calling a method is a lot of overhead when the majority of .NET APIs don't depend on pwd. Doing this also greatly complicates a host application's (e.g. exchange UI, VMM console, etc.) use of the current working directory since it can't depend on PWD anymore. The workaround for .NET APIs that take paths is to, as mentioned in another comment, resolve the path before passing It to the API which need not be onerous e.g. WriteAllText("$pwd\fileincurrentdir.txt")

@mklement0
Copy link
Contributor

mklement0 commented Jun 27, 2018

Too bad that there's no good solution, because the behavior is a perennial pain point.

The "$pwd\fileincurrentdir.txt" shortcut is alluring, but - aside from assuming that the current location is a filesystem location (an assumption that will often, but not always hold) - it fails with current filesystem locations based on PS drives.

Remedying that doesn't take much extra effort, fortunately:

[io.file]::WriteAllText($PWD.ProviderPath + '/fileInCurrentDir.txt', 'foo')

If you also want to guard against the current location being on a different provider's drive:

[io.file]::WriteAllText((Get-Location -PSProvider FileSystem).ProviderPath + '/fileInCurrentDir.txt', 'foo')

@PetSerAl's ConvertTo-FileSystemPath helper function is the most robust and flexible approach, but anything that doesn't ship with PowerShell is unlikely to see widespread adoption.

However, what this function does could be folded into the standard Convert-Path cmdlet - see #7197

@essentialexch
Copy link

I think you've missed a use-case, and that's libraries that don't know anything about PS.

For example, Terminal.Gui has at least three APIs (related, but still - FileDialog, OpenDialog, SaveDialog) that initialize the displayed folder from [system.environment]::currentdirectory. So I have to set it to what PS thinks the working directory is before initializing any of those APIs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Issue-Discussion the issue may not have a clear classification yet. The issue may generate an RFC or may be reclassif Resolution-By Design The reported behavior is by design.
Projects
None yet
Development

No branches or pull requests

6 participants