Skip to content

Set-Location preserves case instead of matching filesystem on case-insensitive/preserving system #5678

Closed

Description

Steps to reproduce

mkdir Foo
cd foo

Expected behavior

    Working directory ends in `\Foo`.

Actual behavior

    Working directory ends in `\foo`. (note the difference in case)

Remarks

I originally thought this issue belonged with #1273, but that is about tab completion and globbing on non-Windows platforms, whereas this does not involve tabbing or globbing, and is on Windows.

This problem can cause problems with git and other things.

If I use cmd.exe instead, it gives the expected/desired results.

On case-sensitive systems, I don't know what the correct behavior should be (reject it if the path doesn't exist, or automagically switch to the fs-matching case)... perhaps it should be controlled by the same settings as in #1273.

Environment data

PS C:\Users\danthom> $PSVersionTable

Name                           Value
----                           -----
PSVersion                      5.1.17047.1000
PSEdition                      Desktop
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0...}
BuildVersion                   10.0.17047.1000
CLRVersion                     4.0.30319.42000
WSManStackVersion              3.0
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1

Activity

  1. SteveL-MSFT commented on Feb 2, 2018

    @SteveL-MSFT
    Member

    Unfortunately, this has turned out to be much more complicated than I anticipated. [DirectoryInfo]::GetFiles() and GetDirectories() aren't working correctly on macOS. I'll need to open a issue for corefx. https://github.com/dotnet/corefx/issues/26797

  2. iSazonov commented on May 8, 2018

    @iSazonov
    Collaborator

    DotNet Issue was closed without resolution. 😕

  3. SteveL-MSFT commented on May 9, 2018

    @SteveL-MSFT
    Member

    I'll investigate this again once I find some time.

  4. iSazonov commented on May 9, 2018

    @iSazonov
    Collaborator

    Maybe we need to follow C# as discussed in #6833 and develop language means for creating portable scripts.

  5. mklement0 commented on Sep 3, 2018

    @mklement0
    Contributor

    Dan Thompson (@jazzdelightsme):

    On case-sensitive systems, I don't know what the correct behavior should be (reject it if the path doesn't exist, or automagically switch to the fs-matching case)...

    There's at least one case in which the path should be rejected:
    If there's ambiguity due to the presence of more than 1 case-variant folder: e.g, if you specify test and both TEST and Test exist.

    If there is no ambiguity - just 1 case-variant match - I suppose targeting that automatically is an option, but note that for consistency we'd then have to implement the same logic wherever file/directory paths are accepted, such as Get-Content.

    I suspect that that behavior would ultimately be too obscure and I suggest we keep a clean separation between:

    • literal names that must be case-exact on case-sensitive filesystems.

    • (possibly opt-in) case-insensitive tab-completion and wildcard matching, where ambiguity can be resolved by cycling through / returning multiple matches.

  6. mklement0 commented on Sep 3, 2018

    @mklement0
    Contributor

    As for the issue at hand:

    macOS:

    https://github.com/dotnet/corefx/issues/26797 is indeed resolved, but the issue at hand persists, and it is PowerShell that is the culprit (in the following examples, a Test directory is created, and it is accessed via its all-lowercase variant, test):

    PS> mkdir -p /tmp/Test; Set-Location /tmp/test; $PWD.Path
    /tmp/test # !! *input* case was preserved, even though the actual name is a case variant

    Using .NET Core does NOT exhibit this behavior:

    PS> mkdir -p /tmp/Test; [Environment]::CurrentDirectory = '/tmp/test'; [Environment]::CurrentDirectory
    /private/tmp/Test  # OK: actual dir. name is reflected in the last path component

    (Note that /tmp is a symlink to /private/tmp on macOS.)

    Windows:

    Unlike on macOS, it is .NET itself and, I suspect, ultimately the Windows API that behaves this way, so if we want to fix this behavior, we'll have to do it in PowerShell (as cmd.exe apparently does):

    PS> $null = mkdir -Force $env:TEMP\Test; [Environment]::CurrentDirectory = "$env:TEMP\test"; [Environment]::CurrentDirectory
    C:\Users\jdoe\AppData\Local\Temp\test  # !! *input* case was preserved.

    Note that the behavior doesn't just affect Set-Location (cd), but also other cmdlets such as Get-Item.

  7. iSazonov commented on Sep 4, 2018

    @iSazonov
    Collaborator

    Michael Klement (@mklement0) Thanks! I agree that it is PowerShell issue.
    Perhaps we should ask a more general question -see #1273 and #7221 last comment) - how much should we be case-sensitive/-insensitive on every platform?

  8. mklement0 commented on Sep 4, 2018

    @mklement0
    Contributor

    Ilya (@iSazonov): Please see #5678 (comment) above.

    In short:

    • I think literal names should never be automatically case-corrected on case-sensitive filesystems, to avoid ambiguity.

    • It makes sense to offer case-insensitivity for (a) tab-completion and (b) filename globbing, but I wonder if it should require an explicit opt-in (perhaps via preference variables), at least for globbing.

      • Otherwise, users may be surprised if Get-Item test doesn't find .\Test, but Get-Item test* does, for instance.

      • For ad-hoc use (as well as for internal use in generating completions), adding a -CaseInsensitive switch to Resolve-Path does make sense.

    That said, as stated in #1273 (comment), the tricky thing is that a preference variable that controls globbing case-sensitivity, if set in the global scope, would potentially change the behavior of all scripts run in-process, unless they explicit set the preference variable to the default in their scope.

    Given that, perhaps the answer is to:

    • categorically use case-insensitive globbing (and tab-completion) on all platforms, even on Linux.

    • offer case-sensitive globbing, if desired, only:

      • via the filesystem provider's -Filter parameter (though that could be confusing)
      • via a -CaseSensitive (sic) switch added to Resolve-Path - for consistency, that switch would then have to be honored on Windows and macOS too, however (just documenting it as being ignored on these platform may not be enough to avoid confusion).
  9. iSazonov commented on Sep 5, 2018

    @iSazonov
    Collaborator

    categorically use case-insensitive globbing (and tab-completion)

    I agree with tab-completion but globbing - I think users will be surprised that Copy-Item -Path $env:TEMP\T* will copy $env:TEMP\test on Linux.

  10. mklement0 commented on Sep 5, 2018

    @mklement0
    Contributor

    There ultimately is no good solution here, but what appeals to me about the latter solution is its simplicity:

    • As a user, all you need to remember is that PowerShell wildcards are case-insensitive everywhere by default.

      • Getting case-sensitive filename globbing, if needed (probably rarely), must be requested explicitly with -Resolve-Path -CaseSensitive and perhaps also the filesystem provider's -Filter parameter.
        • I'm starting to think that the latter is not a good idea, though: on the one hand, there already are differences between -Filter and PS wildcards on Windows, but they are subtle and don't affect the majority use cases; on the other hand, -Filter is often used for performance reasons alone, and having it be case-sensitive is then an inconvenience at best, and a subtle source of bugs at worst.
    • No new preference variables and therefore no associated issues with global state ("convention over configuration").

    Obviously, the behavior would need documenting in about_wildcards.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

Resolution-FixedThe issue is fixed.WG-Engine-Providersbuilt-in PowerShell providers such as FileSystem, Certificates, Registry, etc.Waiting - DotNetCorewaiting on a fix/change in .NET

Type

No type

Projects

No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions