Is there a syntax for the -Filter property of Get-ChildItem to allow you to apply multiple filters at the same time? i.e. something like the below where I want to find a few different but specific named .dll's with the use of a wildcard in both?
Get-ChildItem -Path $myPath -Filter "MyProject.Data*.dll", "EntityFramework*.dll"
or do I need to split this into multiple Get-ChildItem calls? because I'm looking to pipe the result of this.
The -Filter parameter in Get-ChildItem only supports a single string/condition AFAIK. Here's two ways to solve your problem:
You can use the -Include parameter which accepts multiple strings to match. This is slower than -Filter because it does the searching in the cmdlet, while -Filter is done on a provide-level (before the cmdlet gets the results so it can process them). However, it is easy to write and works.
#You have to specify a path to make -Include available, use .\*
Get-ChildItem .\* -Include "MyProject.Data*.dll", "EntityFramework*.dll"
You could also use -Filter to get all DLLs and then filter out the ones you want in a where-statement.
Get-ChildItem -Filter "*.dll" .\* | Where-Object { $_.Name -match '^MyProject.Data.*|^EntityFramework.*' }
You may join the filter results in a pipe like this:
#("MyProject.Data*.dll", "EntityFramework*.dll") | %{ Get-ChildItem -File $myPath -Filter $_ }
You can only use one value with -Filter, whereas -Include can accept multiple values, for example ".dll, *.exe".
You could use both -Filter and -Include at the same time
Get-ChildItem -Path $myPath -Filter "*.dll" -Include "MyProject.Data*.dll", "EntityFramework*.dll"
Related
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.
-Filter only accepts a single pattern.
Use -Include and -Exclude instead[1], 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, -Include and -Exclude are hampered by performance problems, as Mathias R. Jessen notes, due to their inefficient implementation (see GitHub issue #8662), so the Where-Object-based solution discussed below may be called for not just for more complex matching logic, but for performance alone.
-Include and -Exclude use PowerShell's wildcard expressions, which have more features and lack the legacy quirks of the platform-native patterns that -Filter supports - see this answer for more information.
The upside of using -Filter is that its much faster than -Include / -Exclude, because -Filter filters _at the source, whereas -Include / -Exclude filters are applied after the fact, by PowerShell, after all items have been retrieved first.
Without -Recurse, -Include and -Exclude do not work as expected - use Get-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 -Exclude are 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 -Filter parameter.
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.
[1] Note that you may even combine -Filter with -Include / -Exclude, in which case the -Filter argument is applied first, at the source. Because of how -Include and -Exclude currently work, this is only works meaningfully if the input path ends in \* or -Recurse is used - see this answer.
With -not, you would need parentheses because of operator precedence (without parentheses "-not $_.name" becomes "False"):
get-childitem | where {$_.name -like '*.pdf' -and -not ($_.name -like '*Sold*')}
You could also get into wmi for some speed (doesn't cover c:\users?):
Get-WmiObject CIM_DataFile -filter 'Drive="C:" and Extension="pdf" and
not name like "%Sold%" and path like "\\%"'
I am searching for a file in all the folders.
Copyforbuild.bat is available in many places, and I would like to search recursively.
$File = "V:\Myfolder\**\*.CopyForbuild.bat"
How can I do it in PowerShell?
Use the Get-ChildItem cmdlet with the -Recurse switch:
Get-ChildItem -Path V:\Myfolder -Filter CopyForbuild.bat -Recurse -ErrorAction SilentlyContinue -Force
I use this to find files and then have PowerShell display the entire path of the results:
dir -Path C:\FolderName -Filter FileName.fileExtension -Recurse | %{$_.FullName}
You can always use the wildcard * in the FolderName and/or FileName.fileExtension. For example:
dir -Path C:\Folder* -Filter File*.file* -Recurse | %{$_.FullName}
The above example will search any folder in the C:\ drive beginning with the word Folder. So if you have a folder named FolderFoo and FolderBar PowerShell will show results from both of those folders.
The same goes for the file name and file extension. If you want to search for a file with a certain extension, but don't know the name of the file, you can use:
dir -Path C:\FolderName -Filter *.fileExtension -Recurse | %{$_.FullName}
Or vice versa:
dir -Path C:\FolderName -Filter FileName.* -Recurse | %{$_.FullName}
When searching folders where you might get an error based on security (e.g. C:\Users), use the following command:
Get-ChildItem -Path V:\Myfolder -Filter CopyForbuild.bat -Recurse -ErrorAction SilentlyContinue -Force
Here is the method that I finally came up with after struggling:
Get-ChildItem -Recurse -Path path/with/wildc*rds/ -Include file.*
To make the output cleaner (only path), use:
(Get-ChildItem -Recurse -Path path/with/wildc*rds/ -Include file.*).fullname
To get only the first result, use:
(Get-ChildItem -Recurse -Path path/with/wildc*rds/ -Include file.*).fullname | Select -First 1
Now for the important stuff:
To search only for files/directories do not use -File or -Directory (see below why). Instead use this for files:
Get-ChildItem -Recurse -Path ./path*/ -Include name* | where {$_.PSIsContainer -eq $false}
and remove the -eq $false for directories. Do not leave a trailing wildcard like bin/*.
Why not use the built in switches? They are terrible and remove features randomly. For example, in order to use -Include with a file, you must end the path with a wildcard. However, this disables the -Recurse switch without telling you:
Get-ChildItem -File -Recurse -Path ./bin/* -Include *.lib
You'd think that would give you all *.libs in all subdirectories, but it only will search top level of bin.
In order to search for directories, you can use -Directory, but then you must remove the trailing wildcard. For whatever reason, this will not deactivate -Recurse. It is for these reasons that I recommend not using the builtin flags.
You can shorten this command considerably:
Get-ChildItem -Recurse -Path ./path*/ -Include name* | where {$_.PSIsContainer -eq $false}
becomes
gci './path*/' -s -Include 'name*' | where {$_.PSIsContainer -eq $false}
Get-ChildItem is aliased to gci
-Path is default to position 0, so you can just make first argument path
-Recurse is aliased to -s
-Include does not have a shorthand
Use single quotes for spaces in names/paths, so that you can surround the whole command with double quotes and use it in Command Prompt. Doing it the other way around (surround with single quotes) causes errors
Get-ChildItem V:\MyFolder -name -recurse *.CopyForbuild.bat
Will also work
Try this:
Get-ChildItem -Path V:\Myfolder -Filter CopyForbuild.bat -Recurse | Where-Object { $_.Attributes -ne "Directory"}
Filter using wildcards:
Get-ChildItem -Filter CopyForBuild* -Include *.bat,*.cmd -Exclude *.old.cmd,*.old.bat -Recurse
Filtering using a regular expression:
Get-ChildItem -Path "V:\Myfolder" -Recurse
| Where-Object { $_.Name -match '\ACopyForBuild\.[(bat)|(cmd)]\Z' }
To add to #user3303020 answer and output the search results into a file, you can run
Get-ChildItem V:\MyFolder -name -recurse *.CopyForbuild.bat > path_to_results_filename.txt
It may be easier to search for the correct file that way.
On a Windows system:
Search for all .py files in the 'c:\temp' dir and subdirs, type: dir -r *.py or dir *.py -r
On a *Nix (Linux / MacOs system:
at the terminal type: find /temp -name *.py
This works fine for me.
Generally, robocopy is the fastest and simplest way for searching multiple files in parallel threads. It needs a quite good Powersell code with parallelism to beat that. Here is a link to an article I have written in the past with all the different options you have: Fastest way to find a full path of a given file via Powershell? Check the accepted answer for the best code.
When using "-filter":
Get-ChildItem -file -filter "*.txt" | foreach-object { write-host $_.FullName }
I get a listing of the 4 .txt files that's in the current folder.
I tried using "-include"
Get-ChildItem -file -include *.txt | foreach-object { write-host $_.FullName }
Get-ChildItem -file -include *txt | foreach-object { write-host $_.FullName }
and I get nothing. I tried with and without the "-file" parameter and it makes no difference.
I've looked at various guides/examples (ss64.com/TechNet and etc) and supposedly I am doing it right.
Any ideas what I could be doing wrong? Thanks!
From the Get-Help page for Get-ChildItem:
The -Include parameter is effective only when the command includes the -Recurse parameter or the path leads to the contents of a directory, such as C:\Windows*, where the "*" wildcard character specifies the contents of the C:\Windows directory.
You'll note that you don't get a syntax error if you specify -include and don't specify -recurse in spite of the fact that whatever it does is literally undefined. You'll also note that C:\Windows* is not a normal wildcard expression for "all files in the C:\Windows directory". It's a wildcard expression for "all items that start with 'Windows' in the C:\ directory and may or may not have an extension". I have no idea what the authors of Get-ChildItem think this parameter is supposed to do. They've done a fantastically poor job of documenting it and implementing it.
Consequently, I avoid the -Include parameter as broken/badly documented. I don't know what it's supposed to do that -Filter doesn't. I've read articles about what it does exactly. It "passes the value to the underlying provider to filter at that level" in some manner. I don't know why they assume that a sysadmin will know what that really means. My understanding is that it's the difference between calling DirectoryInfo.GetFiles() on each directory item and calling DirectoryInfo.GetFiles('*.txt') on each directory item, but most sysadmins aren't going to know what that means. However, it's so oddly behaved that I don't trust it, so even though I am about 95% sure of what it does... I still never use it.
Instead, I just pipe to Where-Object:
Get-ChildItem -file | Where-Object Extension -eq '.txt' | [...]
Also note that Get-ChildItem is broken with -LiteralPath, -Recurse and -Include in some versions of PowerShell, and will instead return all items.
Compare:
Get-ChildItem -LiteralPath $PSHOME *.exe -Recurse # works
Get-ChildItem -Path $PSHOME -Include *.exe -Recurse # works
Get-ChildItem -LiteralPath $PSHOME -Include *.exe -Recurse # does NOT work
Issue reported here for v6.
These work for me without recursion:
Get-ChildItem -Path "C:\Users\Athom\Desktop\*.txt"
Get-ChildItem -Path ".\*.txt"
Or Just add the recursion parameter:
Get-ChildItem -Include *.txt -Recurse
I am trying to select files that have identical names except for the extenstion...
IE:
idiotCode.dll, idiotCode.pdb, idiotCode.xml, StupidFool.dll, StupidFool.pdb, StupidFool.xml
et cetera.
take a gander at my where-Object call in the below script line...
gci -path $FromPath | ? {$_.Name -match "idiotCode|StupidFool|YourAnIdiot|TheSuckIstHeMatterWhichU" -and $_.Name -like "*.dll"} | foreach{write-host("Do I have the files here? : "+ $_.Fullname + " -destination" + $ToPath) }
Can I use the like parameter to do that? Is there another way to do that? Maybe something in my get-childItem method call which I could pipe into my Where-Object call?
There is a lot that you can do with just the Get-ChildItem Cmdlet. If you look at the help Get-ChildItem, you can do a lot of the filtering there. Specifically using the filters -Filter, -Include and -Exclude
For ex:
Get-ChildItem -Path $FromPath -Include "idiotCode.*","StupidFool.*","YourAnIdiot.*","TheSuckIstHeMatterWhichU.*" -Filter "*.dll"
Ok, I'm a little confused as to the whole concept here because you say you want to select multiple files of the same name but different extensions. Then you pull a directory listing of PathA, filter out everything except files in a kind of "approved names list", and only allow .DLL files to show, and then reference PathB for reasons unknown.
I'm guessing here, but I think you want to query .DLL files from PathA, and then check for matching files from PathB.
$Reference = (GCI $FromPath -Filter "*.DLL").basename
GCI $ToPath|?{$_.BaseName -Match $Reference}|FT -Group BaseName
I would like to search for a folder in a specific directory and subdirectorys.
I tried googling it, but didn't really find any usefull examples.
Get-ChildItem C:\test -recurse | Where-Object {$_.PSIsContainer -eq $true -and $_.Name -match "keyword"}
I believe there's no dedicated cmdlet for searching files.
Edit in response to #Notorious comment:
Since Powershell 3.0 this is much easier, since switches -Directory and -File were added to Get-ChildItem. So if you want it short you've got:
ls c:\test *key* -Recurse -Directory
With command alias and tab-completion for switches it's a snap. I just missed that the first time.
Here is my version, which is only slightly different:
gci -Recurse -Filter "your_folder_name" -Directory -ErrorAction SilentlyContinue -Path "C:\"
some more info:
-Filter "your_folder_name"
From documentation: Filters are more efficient than other parameters. The provider applies filter when the cmdlet gets the objects rather than having PowerShell filter the objects after they're retrieved. The filter string is passed to the .NET API to enumerate files. The API only supports * and ? wildcards.
-Directory
Only examine directories, also could be -File
-ErrorAction SilentlyContinue
Silences any warnings
-Path "C:\"
Specifies a path to start searching from
https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.management/get-childitem?view=powershell-7