TL;DR: gci -r -fi <filename-pattern>
My favorite use of find
in bash is to find files whose name matches a pattern. For instance, find all the jar files under this directory:
bash# find . -name '*.jar'
In PowerShell, there’s a program called “find” but it ain’t the same program.
Short Answer
Instead, use Get-ChildItem
. The arguments are different.
-Recurse
(abbreviates to-r
) says, go down all the directories.-
-Filter <string>
(abbreviates to-fi
) selects by name. This supports * and ? wildcards, NOT regex.
Long version, which I would use in programs:
pwsh> Get-ChildItem -Recurse -Filter '*.jar'
or shorthand, for typing at the prompt:
pwsh> gci -r -fi *.jar
but I want regex
The -Filter
command-line option here is an optimization. For programmatic filtering: use Get-ChildItem -Recurse
to gather all the files under the current directory, and pipe them to a Where-Object
(abbreviates to where
) filter.
You can test properties, like matching the name against a regex:
gci -r | where Name -match '\.jar$'
like checking the size of the file, excluding too-small ones:
gci -r | where Length -gt 3000
like only the files I’ve looked at in the last hour:
gci -r | where LastAccessTime -gt (Get-Date).AddHours(-1)
In my PowerShell in Windows Terminal, I get tab-completion for the property names! This is based on the type of objects returned by the gci
command before the pipe. Whoa cool!
For more flexibility, you can break into a code block, referencing the input with $_
. How about… length greater than 3kb or else I wrote to it since a specific day:
gci -r | where { $_.Length -gt ( 3 * 1024 ) -or $_.LastWriteTime -gt "04/23/2020" }
but that’s so long
The query for “size is large enough or I’ve accessed it in the last hour” works in bash too (I think):
bash# find . -size +3k -o -amin +60
There are two reasons I like the PowerShell version better than bash’s find
:
- it scales up in complexity; you can write a whole program in there if you need to.
find
offers the conditions that it offers, and the combinations that it offers. (If you can say “name is *.jar and size is large or date is recent”, I couldn’t figure out how.) - The arguments to
find
are specific tofind
. Once you learn them, that knowledge doesn’t help you with any other command in bash. In PowerShell, the condition is expressed in the same language as everything else in PowerShell. That scales, in your head.
There’s one reason I like bash’s find
better than PowerShell syntax:
I can type find . -name '*.jar'
without thinking about it.
I have it memorized. It is familiar. That’s a property of me; it’s my shared history with bash that makes me productive in it, NOT its superiority.
So I don’t recommend going deep on bash. I recommend getting productive in PowerShell instead, because learning scales in a language with deep consistency.
Addendum: condense the output
The output of Get-ChildItem
is a bunch of FileInfo
objects. The default printing takes up a lot of room. You can get it to print the path (which is called FullName
):
Get-ChildItem -Recurse -Include '*.json' | Select FullName
You can also get other properties from Select
. To see them all, pipe to Get-Member
instead. That lists available properties.