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

Need examples on when/how to use wild card in path with -Recurse #10640

Open
2 tasks done
SteveL-MSFT opened this issue Nov 15, 2023 · 7 comments
Open
2 tasks done

Need examples on when/how to use wild card in path with -Recurse #10640

SteveL-MSFT opened this issue Nov 15, 2023 · 7 comments
Assignees
Labels
area-management Area - Microsoft.PowerShell.Management module issue-doc-idea Issue - request for new content

Comments

@SteveL-MSFT
Copy link
Contributor

Prerequisites

  • Existing Issue: Search the existing issues for this repository. If there is an issue that fits your needs do not file a new one. Subscribe, react, or comment on that issue instead.
  • Descriptive Title: Write the title for this issue as a short synopsis. If possible, provide context. For example, "Document new Get-Foo cmdlet" instead of "New cmdlet."

PowerShell Version

5.1, 7.2, 7.3, 7.4

Summary

When using a wildcard as part of -Path w/ -Recurse, it might be surprising to users that the wild card applies to -Path before -Recurse so it only recurses into paths that match the wildcard rather than recursing into all folders and then applying the path wildcard.

Details

PowerShell/PowerShell#20138

Proposed Content Type

Cmdlet Reference

Proposed Title

No response

Related Articles

No response

@SteveL-MSFT SteveL-MSFT added issue-doc-idea Issue - request for new content needs-triage Waiting - Needs triage labels Nov 15, 2023
@sdwheeler sdwheeler self-assigned this Nov 15, 2023
@sdwheeler sdwheeler added area-management Area - Microsoft.PowerShell.Management module and removed needs-triage Waiting - Needs triage labels Nov 15, 2023
@mklement0
Copy link
Contributor

mklement0 commented Nov 15, 2023

Here is a summary of the de facto behavior and its pitfalls:

  • Get-ChildItem -Recurse with a -Path argument but without -Name:

    • Searches for the last path component - whether or not is an actual wildcard pattern or a literal file/directory name - on every level of the target (parent) directory's hierarchy.
    • The only exception is if the last path component matches existing immediate subdirectories of the target directory, in which regular all-content recursion is performed on these directories.
    • In other words:
      • The recursion logic situationally changes fundamentally, depending on whether the last path component matches existing immediate child directories or not.
      • For last path components matching files as well as those not matching any immediate child items, at-every-level-matching is performed. If directories are among the results, they are reported as themselves rather than by their content.
  • With -Name, the logic is again different, as discussed in why are subfolders not evaluated even though force and recursive are used PowerShell/PowerShell#20138

    • Matching of the last path component is only performed against the target (parent) directory's immediate children.
    • Regular content recursion is then performed on any matching directories.
    • In other words:
      • Wildcard matching happens only once, against the target (parent) directory's immediate content, and the results are then processed as if they had been passed (individually) via -LiteralPath.
      • If the last path component matches no items among the target directory's immediate children, an error occurs.
    • Arguably, this is the much preferable approach:
      • It avoids the complex and treacherous behavior of the non--Name behavior.
      • It is easier to explain and conceptualize.
    • Of course, that the non--Name and the -Name behaviors differ is a problem in itself; unfortunately, it was decided to keep both behaviors.

@mklement0
Copy link
Contributor

mklement0 commented Nov 15, 2023

In light of the above intricacies, I suggest providing the following guidance with respect to use of -Recurse:

  • Always use -LiteralPath to specify the target directory. This avoids accidentally triggering a recursive search for the last path component itself.

  • Use -Filter or -Recurse to specify patterns (including literal names) that should be searched for on every level of the target directory's subtree.

For example:

Instead of:

# -Path implied
Get-ChildItem C:\path\to\package*.json -Recurse

use:

Get-ChildItem -LiteralPath C:\path\to -Filter package*.json -Recurse

This separation provides an unambiguous and conceptually clear solution that avoids the pitfalls above.

@mklement0

This comment was marked as resolved.

@TobiasPSP
Copy link

TobiasPSP commented Nov 17, 2023

I commented to the original thread in more detail.

Would it be safe to assume that these three guidances eliminate unexpected results:

  • do not use wildcards in -Path, or use -LiteralPath instead where wildcards are not permitted by design
  • Use wildcards instead with -Filter or -Include
  • with -Path, use absolute path names, or else fully understand how relative path names are interpreted

@mklement0
Copy link
Contributor

mklement0 commented Nov 17, 2023

  • I think the advice needs to be more stringent with respect to -Path, as it isn't just actual wildcard expressions that are the problem - see the scenario below.

  • I don't think relative paths are a problem; the at-every-level recursion pitfall applies equally to relative and full paths - again, see below.

In other words: I think the advice in my previous comment is both stringent enough and sufficient.


A treacherous non-wildcard scenario:

The problem is that the last component - whether it contains wildcard metacharacters or not - is always searched for at every level of the target directory subtree if (a) it matches the name of a file among the immediate children of the target directory or (b) it matches nothing among the immediate children.

  • Assume you have a project root directory containing containing many Node.js projects, each of which have node_modules subfolders.
  • Say your intent is to (recursively) delete the contents of a specific project's node_modules subdirectory, without removing the directory itself, and you submit the following:
      # -Path implied
      Get-ChildItem .\node_modules -Recurse |  Remove-Item -Recurse
  • If you indeed are in the project directory of interest, life is good; the command is then effectively equivalent to:
      Get-ChildItem -LiteralPath .\node_modules -Recurse |  Remove-Item -Recurse
  • If you are not and there happens to be no node_modules subdir., the command is equivalent to:
      Get-ChildItem -LiteralPath . -Filter node_modules -Recurse |  Remove-Item -Recurse

That is, instead of deleting a single node_module directory's content, you're now effectively removing all node_modules directories as a whole, in the entire subtree of the current directory.

Even though a relative path is used in the above example, the same applies to an absolute one; let's say $HOME is your project root directory, and you forget to type a path component, say you type, $HOME\node_modules instead of $HOME\SomeProject\node_modules:

      # !! Same match-at-every-level logic as with .\node_modules, if $HOME happens not to contain node_modules itself.
      Get-ChildItem $HOME\node_modules -Recurse |  Remove-Item -Recurse

Perhaps needless to say, this behavior can have disastrous consequences, with the likelihood increasing the deeper the (possibly implicitly) targeted directory subtree is; in the extreme case, Get-ChildItem C:\SomeDir will search the entire C: drive for SomeDir items if C:\SomeDir itself doesn't exist.

@TobiasPSP
Copy link

Thank you for your scenario. I am carrying it into the cmdlet workgroup for discussion and keep everyone updated here.

@SteveL-MSFT
Copy link
Contributor Author

CmdletsWG discussed this. We think the docs should leverage the content @mklement0 provided starting with the scenario of deleting a file recursively and then describing the behavior with each one and recommending the most explicit one to ensure the user understands what will happen.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-management Area - Microsoft.PowerShell.Management module issue-doc-idea Issue - request for new content
Projects
None yet
Development

No branches or pull requests

4 participants