Search code examples
windowspowershellget-childitem

Powershell Get-ChildItem: how to get everything with a given BaseName, both Files and Folders?


I have this directory/file structure:

S:\My Folder\BaseName 1\
S:\My Folder\BaseName 1\BaseName 1 1.lnk
S:\My Folder\BaseName 1\BaseName 1 1.txt
S:\My Folder\BaseName 1.lnk
S:\My Folder\BaseName 1.txt
S:\My Folder\BaseName 2\
S:\My Folder\BaseName 2\BaseName 2 1.lnk
S:\My Folder\BaseName 2\BaseName 2 1.txt
S:\My Folder\BaseName 2.lnk
S:\My Folder\BaseName 2.txt

I have a script that seeks for *.lnk files in directorys and then should handle any other file/folder with the same BaseName of the *.lnk found.

When the script parses *S:\My Folder* it finds BaseName 1.lnk and BaseName 2.lnk

Now I need to find all the child-items in S:\My Folder (and only there, not in the subfolders) with the same BaseNames, so I need to find both the BaseName 1 folder and the BaseName 1.txt file.

But i'm unable to do it:

  1. Get-ChildItem -Path ".\BaseName 1" -exclude *.lnk returns the content of the directory BaseName 1, which i don't want.
  2. Get-ChildItem -Path ".\BaseName 1*" -exclude *.lnk returns only BaseName 1.txt and the content of the directory BaseName 1, which i don't want.
  3. Get-ChildItem -Path ".\BaseName 1.*" -exclude *.lnk returns only BaseName 1.txt but not the directory BaseName 1, which i need.
  4. Get-ChildItem -Path "." -include "BaseName 1" -exclude *.lnk -recurse returns only the the directory BaseName 1, but not the BaseName 1.txt file
  5. Get-ChildItem -Path "." -include "BaseName 1*" -exclude *.lnk -recurse returns everything, but again, i don't want the content of the directory BaseName 1

What should i do?


Solution

  • Use Get-Item rather than Get-ChildItem:

    Get-Item -Path '.\BaseName 1*'  -Exclude *.lnk
    

    More stringently (given that the above would also match .\BaseName 11, for instance):

    # Add -ErrorAction Ignore to silence a potential error if no
    # item literally named 'BaseName 1' exists.
    Get-Item -Path '.\BaseName 1', '.\BaseName 1.*'  -Exclude *.lnk
    

    As for what you tried:

    # !! Includes the *child* items of dir. 'BaseName 1', due to use of -Exclude
    Get-ChildItem -Path ".\BaseName 1*"  -Exclude *.lnk
    

    Leaving aside that Get-Item is also conceptually the better choice, since you're not looking for child items of what the wildcard expression ".\BaseName 1*" matches, the behavior you've observed when combining Get-ChildItem with -Exclude is certainly surprising (observed in Windows PowerShell v5.1 and PowerShell (Core) 7 as of v7.5.0):

    • By default, a directory matched by wildcard is reported as itself rather than reporting its child items (the latter always happens if you use a literal name).

    • The addition of -Exclude surprisingly implicitly modifies this behavior to report the children and applying the exclusion to them.

      • Judging by the docs, as of this writing, it is unclear if this is by design; either way, the behavior is counterintuitive and obscure.
    • If -Include is used, the behavior becomes downright buggy:

      • In addition to unexpectedly triggering reporting of the children, as with -Exclude, the pattern is first matched against the item name itself, and if it doesn't match, the children are not matched.

      • E.g., Get-ChildItem foo* -Include *.txt never matches anything, because *.txt cannot match whatever directory names foo* resolved to.

    • A summary of all problematic -Include / -Exclude behaviors can be found in the bottom section of this answer.