Skip to content

Unix-like platforms, Windows with the experimental PSNativeWindowsTildeExpansion feature: Native tilde expansion unexpectedly expands tilde in variable references #21323

Open

Description

Prerequisites

Steps to reproduce

See also:

The expansion of an unquoted, literal, standalone ~ or of an unquoted, literal, ~/ prefix to the value of $HOME / "$HOME/" when calling native (external) programs is meant to emulate the tilde shell expansion feature in POSIX-compatible shells such as Bash.

In such shells tilde expansion is NOT performed when a path argument is provided via a variable.

PowerShell should exhibit the same behavior, but currently performs expansion nonetheless (unless the variable reference itself happens to be "..."-enclosed):

# Bash behavior, for contrast:
# ~ is NOT expanded.
bash -c 'p="~"; echo $p'

# !! PowerShell DOES expand
$p = '~'
/bin/echo $p

Expected behavior

~
~

Actual behavior

~
/home/jdoe

Error details

No response

Environment data

PowerShell 7.5.0-preview.2

Visuals

No response

Activity

  1. MatejKafka commented on Mar 10, 2024

    @MatejKafka
    Contributor

    Problem is that when you don't expand the ~ when calling native command with variable arguments, ~ stops working for PowerShell functions which internally call native commands, which might be surprising. As much as I despise the behavior, I see the rationale for the current design.

  2. mklement0 commented on Mar 10, 2024

    @mklement0
    ContributorAuthor

    Matěj Kafka (@MatejKafka), there's an unresolvable tension when different worlds collide, and I understand that there's no ideal solution here:

    Introducing POSIX-like shell expansions invariably introduces a concept that is foreign to PowerShell: that whether characters such as ~ and * are individually escaped / inside quoted strings or not matters.

    To PowerShell itself, these distinctions do not matter, given that ~ and * are not metacharacters at the shell level - they are passed as-is to the target PowerShell command, which may or may not choose to assign special meaning to them - such as an initial ~ referring to the underlying provider's home location in provider cmdlets, and * representing a wildcard metacharacter in arguments passed to parameters that happen to support wildcard expressions.

    If PowerShell were to strictly adhere to this model, neither ls ~ nor ls *.txt would work on Unix-like platforms, given that the /bin/ls utility neither knows what ~ refers to nor can locate a file-system item literally named *.txt.

    For a more seamless integration of external utilities (aka native programs) on Unix, PowerShell (Core) has from the beginning decided to emulate these specific shell expansion (tilde expansion and pathname expansion (globbing)), which invariably entailed introducing the aforementioned foreign concept into PowerShell; without it, you couldn't distinguish between - say - using *.txt to mean "all files in the current directory with extension .txt" and verbatim *.txt

    These concepts are more foreign to Windows users:

    • With respect to pathname expansion (globbing), the issue doesn't arise: this emulation can never be introduced on Windows, as it would break backward compatibility: external utilities on Windows that rely on globbing must perform it themselves, and PowerShell suddenly doing it for them and passing multiple arguments that result from globbing, say, *.txt could break such utilities.

    • However, tilde expansion is less foreign: ~ does have established semantics in PowerShell (but not Windows in general, and not in cmd.exe): it refers to a provider's home location, which notably doesn't necessarily mean the file-system provider's home location, but that's a separate issue: in the context of external utilities, it only ever makes sense to interpret ~ as the file-system's home location, i.e. as $HOME.

      • As such, extending these semantics to external utilities is a more natural extension - even though the escaping/quoting part isn't.
      • The awkwardness comes from needing to understand the latter part, which includes knowing that unquoted ~ therefore cannot be used in variable values, because PowerShell inherently doesn't allow defining string variables without quoting.
        • By contrast, in POSIX-like shells, you can use unquoted values in variable assignments, and therefore take advantage of the usual shell expansions; e.g., p=~ is equivalent to p="$HOME"
        • You cannot do the same in PowerShell, and therefore need to know that ~ in argument mode, when calling external utilities, is the same as $HOME, so in variable assignments $p = $HOME is required.
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

    Needs-TriageThe issue is new and needs to be triaged by a work group.WG-Languageparser, language semanticsWG-NeedsReviewNeeds a review by the labeled Working Group

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions