I'm trying to move .pdf's based on text that could be present anywhere in the file name and it's not working. It executes without error, but it doesn't move the files.
All the .pdf's with 'Sold' in the filename go to one place and all the ones without 'sold' in the file name go to a different place.
get-childitem -Recurse -path $sourcePathPDF {-filter '*.pdf' -and '*Sold*'} | move-item -Destination $destinationPathSoldPDF
get-childitem -Recurse -path $sourcePathPDF {-filter '*.pdf' -not '*Sold*'} | move-item -Destination $destinationPathPDF
Am I filtering/using the -not incorrectly?
Thanks for the help.
CodePudding user response:
-Filteronly accepts a single pattern.Use
-Includeand-Excludeinstead, both of which accept multiple patterns.
# Two inclusion patterns
Get-ChildItem -Recurse -Path $sourcePathPDF -Include *.pdf, *sold*
# One inclusion, one exclusion pattern.
Get-ChildItem -Recurse -Path $sourcePathPDF -Include *.pdf -Exclude *Sold*
Note:
As of PowerShell 7.2,
-Includeand-Excludeare hampered by performance problems, as Mathias R. Jessen notes, due to their inefficient implementation (see GitHub issue #8662), so theWhere-Object-based solution discussed below may be called for not just for more complex matching logic, but for performance alone.-Includeand-Excludeuse PowerShell's wildcard expressions, which have more features and lack the legacy quirks of the platform-native patterns that-Filtersupports - see this answer for more information.- The upside of using
-Filteris that its much faster than-Include/-Exclude, because-Filterfilters _at the source, whereas-Include/-Excludefilters are applied after the fact, by PowerShell, after all items have been retrieved first.
- The upside of using
Without
-Recurse,-Includeand-Excludedo not work as expected - useGet-Item * -Include/-Exclude ...instead - see this answer.
For more sophisticated pattern matching and/or better performance, pipe to a Where-Object call in whose script block you can use the wildcard-based -like operator, as Mathias recommends; alternatively, for even more flexible matching, you can use the regex-based -match operator. To adapt Mathias' -like-based Where-Object solution from the comment:
Get-ChildItem -Recurse -Path $sourcePathPDF |
Where-Object { $_.Name -like '*.pdf' -and $_.Name -notlike '*Sold*' }
For (currently) best performance you can pre-filter with -Filter:
Get-ChildItem -Recurse -Path $sourcePathPDF -Filter *.pdf |
Where-Object { $_.Name -notlike '*Sold*' }
As for what you tried:
-Filter,-Include, and-Excludeare string-typed - there is no support for passing script blocks ({ ... }with arbitrary expression to them.- While pipeline-binding parameters may accept script blocks, in order to supply parameter values dynamically, based on the input object at hand (a feature known as delay-bind script blocks), neither of these parameters support such binding, so passing script blocks doesn't apply.
What actually happened in your attempt is that the stringified version of script block
{-filter '*.pdf' -and '*Sold*'}- which is its verbatim content (excluding{and}) - was positionally bound to the-Filterparameter.- That is, you effectively passed
-Filter "-filter '*.pdf' -and '*Sold*'", which predictably didn't match any files, because verbatim-filter '*.pdf' -and '*Sold*'was used as the pattern.
- That is, you effectively passed
