Skip to content

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

Closed

Description

@antiufo

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.

Activity

  1. PetSerAl commented on Mar 26, 2017

    @PetSerAl
    Contributor

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

  2. antiufo commented on Mar 26, 2017

    @antiufo
    Author

    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.
  3. added
    Issue-Discussionthe issue may not have a clear classification yet. The issue may generate an RFC or may be reclassif
    on Mar 26, 2017
  4. PetSerAl commented on Mar 26, 2017

    @PetSerAl
    Contributor

    antiufo (@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
  5. BrucePay commented on Mar 28, 2017

    @BrucePay
    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")

  6. mklement0 commented on Jun 27, 2018

    @mklement0
    Contributor

    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

  7. essentialexch commented on Dec 14, 2023

    @essentialexch

    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

Metadata

Metadata

Assignees

No one assigned

    Labels

    Issue-Discussionthe issue may not have a clear classification yet. The issue may generate an RFC or may be reclassifResolution-By DesignThe reported behavior is by design.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions