Skip to content

Output-to-file cmdlets should not support wildcards with -Path #6714

Description

Note: This is technically a breaking change; if it is deemed too risky, keep it on file for PowerShell vNext.

Currently, output-to-file cmdlets such as Out-File and Set-Content do support wildcard expression passed to their -Path parameter.

This behavior is problematic in two respects:

  • Overwriting existing files should be a very deliberate act; the use of wildcards to target whatever happens to match contradicts that.

  • The utility of targeting an output file implicitly by wildcard is very limited:

    • If no existing file matches the wildcard expressions, an error occurs.
    • If more than one file matches the wildcard expression, the behavior is inconsistent:
      • With some cmdlets, an error occurs (e.g., Out-File, Export-FormatData
        • In other words: only if the wildcard expression happens to match one existing file does the operation succeed - at which point the clearer and safer choice is to target that file with a literal path.
      • Other cmdlets (e.g.Set-Content) output to all matching files, which is also problematic.

Ergo: output-to-file cmdlets should treat -Path as an alias of -LiteralPath (similar to how -Path in New-Item is effectively -LiteralPath).

Environment data

Written as of:

PowerShell Core v6.0.2

Activity

  1. BrucePay commented on Apr 24, 2018

    @BrucePay
    Collaborator

    Wildcard use is very handy in interactive scenarios. In scripting scenarios, you can use -Literalpath to be precise and avoid wildcard processing.

  2. mklement0 commented on Apr 24, 2018

    @mklement0
    ContributorAuthor

    While it makes sense to distinguish between interactive and script use with respect to syntax, I think extending that distinction to features is problematic.

    Yes, you can view doing something like

    'hi' | Set-Content *.txt

    to overwrite all *.txt files in the current folder as convenient, but I think of it as risky, and the use case strikes me as too exotic to outweigh the risks.

    At the very least, all output-to-file cmdlets should act consistently in such a scenario, in favor of the only-output-to-ONE-file behavior (as Out-File currently does) ; even documenting the differences won't do, in my opinion, because it's just too hard to remember which cmdlet behaves how.

  3. BrucePay commented on Apr 24, 2018

    @BrucePay
    Collaborator

    The wildcard need not be in the leaf of the path. Consider

    'hi' | setcontent longdir*/foo.txt
    

    I definitely agree that having Set-Content write to multiple files seems dubious at best. Kenneth Hansen (@khansen00) may remember why we decided to do this.

    Wildcard expansion everywhere is intended to mimic traditional shell behaviour. Since it was the shell that did expansion, commands had no control of it and the user had to do explicit quoting. Quoting wildcards in paths in PowerShell is problematic since the expansion is handled in the API, not the shell directly and so you have to double-quote all wildcards. This was such a pain we ended up adding -LiteralPath in V2 (not my favourite thing)

  4. ThreeFive-O commented on Apr 24, 2018

    @ThreeFive-O
    Contributor

    I once started a similar discussion #3174 which was committee reviewed therefore.

    While my personal preference would be to treat them as literal paths, I think the current behavior is valid for scripting.

    Having wildcard support for input and output even makes it more consistent.

    BUT, all cmdlets should behave the same! Apart from file path wildcards, literal paths should be available too. I would go as far to say -Path is ALWAYS with wildcard support and -LiteralPath is a mandatory override.

    I have lots of files with square brackets. The way PS handles paths with wildcards always gets me. But that was the initial design of PS and people are using it that way. Maybe something to re-consider in PS7 😀

  5. mklement0 commented on Apr 25, 2018

    @mklement0
    ContributorAuthor

    Bruce Payette (@BrucePay): Good point about the wildcard in a directory component; that's perhaps a better use case.

    I'll close this issue to discuss the problematic Set-Content behavior separately.

    ThreeFive-O:

    BUT, all cmdlets should behave the same!

    Agreed.

    -Path is ALWAYS with wildcard support and -LiteralPath is a mandatory override.

    This distinction is worth observing in general, but there are exceptions, such as with New-Item, where wildcards aren't supported (and make no sense); however, -LiteralPath should be introduced as an alias parameter name there.

    The way PS handles paths with wildcards always gets me. But that was the initial design of PS and people are using it that way. Maybe something to re-consider in PS7

    ThreeFive-O, Bruce Payette (@BrucePay):

    Yes, the great -LiteralPath / -Path schism is unfortunate and a perennial pain point.

    so you have to double-quote all wildcards.

    Bruce Payette (@BrucePay), I assume you meant `-escaping individual wildcard characters inside a single-quoted string or doubly escaping them inside an unquoted or double-quoted string (the alternative is to use [System.Management.Automation.WildcardPattern]::Escape()):

    New-Item -Type File 'tmp[a].txt'  # create file whose name *looks* like a wildcard pattern
    
    # BROKEN, because even though the path matches literally, it is interpreted as a wildcard pattern (by default, `-Path` implied).
    # What matters is what Get-Item ends up seeing *after* string expansion / quote removal.
    Get-Item tmp[a].txt    # same as: Get-Item -Path tmp[a].txt
    Get-Item 'tmp[a].txt'  # same
    Get-Item "tmp[a].txt"  # same
    
    # OK: All of the following target file 'tmp[a].txt' by its exact, literal name:
    Get-Item -Path        'tmp`[a`].txt' # Note the '...' and the individually `-escaped wildcard chars.
    Get-Item -Path        "tmp``[a``].txt" # Note the '"..." and the *doubly* individually `-escaped wildcard chars.
    Get-Item -Path         tmp``[a``].txt # ditto with no outer quoting, which is like implicit "..."
    Get-Item -LiteralPath  tmp[a].txt    # No quoting necessary
    Get-Item -Path ([System.Management.Automation.WildcardPattern]::Escape('tmp[a].txt')) # API use
  6. mklement0 commented on Apr 25, 2018

    @mklement0
    ContributorAuthor

    Closed in favor of #6729.

  7. added
    Issue-Discussionthe issue may not have a clear classification yet. The issue may generate an RFC or may be reclassif
    on May 26, 2018
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-AnsweredThe question is answered.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions