Skip to content

Repository files navigation

winfind

A Unix-like find command for Windows, written in Go.

Rename the binary to anything you like — all usage and error messages derive from the executable name automatically.

Installation

go install (recommended)

Requires Go 1.21+.

go install github.com/fermat-tech/winfind@latest

The binary is placed in your $GOPATH/bin (usually %USERPROFILE%\go\bin), which should already be on your PATH.

Download

Grab the latest binary from Releases and put it somewhere on your PATH.

Build from source

git clone https://github.com/fermat-tech/winfind.git
cd winfind
go build -o winfind.exe .

Usage

winfind [path...] [expression]

Paths default to . if omitted. -h / --help prints usage and --version prints version information.

Predicates

Flag Description
-name PATTERN Filename glob (case-sensitive)
-iname PATTERN Filename glob (case-insensitive)
-path PATTERN Full path glob
-ipath PATTERN Full path glob (case-insensitive)
-type f|d|l File, directory, or symlink
-size [+/-]N[ckMG] Size filter — c=bytes, k=KB, M=MB, G=GB; +N means greater than, -N means less than
-mtime [+/-]N Modified N days ago; +N=older than, -N=newer than
-newer FILE Modified more recently than FILE
-empty Empty file or empty directory
-maxdepth N Descend at most N directory levels
-mindepth N Skip entries fewer than N levels deep

Pattern matching

-name, -iname, -path and -ipath take fnmatch(3) globs — the same ones GNU find uses:

* any run of characters, including none
? any single character
[abc], [a-z] any one of the listed characters
[!abc], [^abc] any character not listed
[[:digit:]] a POSIX class: alpha digit alnum upper lower space blank punct print graph cntrl xdigit

* and ? cross directory separators, as in GNU find, so -path 'src*main.go' matches src\cmd\main.go. A malformed pattern — a trailing \, or a [ with no closing ] — matches nothing rather than reporting an error, which is what glibc does. -iname and -ipath fold case by lowercasing both sides, so a range like [A-Z] becomes [a-z] under them.

Backslashes

A backslash means one thing to -name and another to -path, because on Windows the two need different things:

-name, -iname \ escapes the next character, as in GNU find. -name "*\.yml" matches a literal dot, and -name "v\*.c" finds a file named v*.c. A Windows filename cannot contain a separator, so a backslash here can only have been meant as an escape.
-path, -ipath \ is a path separator, not an escape, so -path "*\sub\*" means what it plainly says. Both separators are accepted and mean the same thing: -path "*\sub\*" and -path "*/sub/*" match the same files.

The -path rule is a deliberate divergence from GNU find, which reads -path '*\sub\*' as the escaped letters *sub*. -path matches the path as winfind prints it, and on Windows that path is spelled with backslashes.

One consequence worth knowing: paths print without a ./ prefix, so a pattern anchored at the top of the tree needs no leading *. When sub sits directly under the starting point, use -path "sub\*"-path "*\sub\*" finds nothing, because there is nothing ahead of sub for the * to match.

Operators

Higher to lower precedence:

! PRED              negate
-not PRED           negate (long form)
PRED1 PRED2         implicit AND
PRED1 -and PRED2    explicit AND
PRED1 -or  PRED2    OR
( PRED )            grouping

Actions

If no action is specified, -print is the default.

Flag Description
-print Print path followed by newline
-print0 Print path followed by null byte (for use with xargs -0)
-ls Print size, mode, modification time, and path
-delete Delete matched file or empty directory
-exec CMD {} ; Run CMD for each match — {} is replaced by the path
-printf FORMAT Print FORMAT for each match, expanding \ escapes and % directives

-printf

-printf does not add a trailing newline — end the format with \n if you want one. Field widths and precisions work as in C's printf, so %-40p left- justifies the path in a 40-column field and %.10f truncates the basename to ten characters.

Directives

%p path %f basename %h leading directories
%P path with the starting point removed %H the starting point %d depth below the starting point (0 = the starting point)
%s size in bytes %b disk space in 512-byte blocks %k disk space in 1 KiB blocks
%S sparseness (allocated ÷ apparent) %n hard link count %i file index (see below)
%D volume serial number %F file system type %l symlink or junction target
%m permission bits, octal %M permissions, ls -l style %y type letter
%Y type letter, following links %u owner name %U owner SID
%g group name %G group SID %Z SELinux context (always empty)
%t modification time %a access time %c creation time
%% a literal %

Type letters for %y are f regular, d directory, l link, p FIFO, s socket, c character device, b block device, ? unknown. %Y resolves the link and reports the target's letter instead, or N if the target is missing, L if the links form a cycle, and ? for any other failure.

%t, %a and %c print a ctime(3)-style stamp with a fractional second: Wed Apr 28 22:22:05.1234567000 2004.

Times

%Tk, %Ak, %Ck and %Bk print the modification, access, creation and birth time using a single format character k:

@ seconds since the epoch, with a fractional part
H I k l M p r S T + X Z time fields
a A b B c d D e F h j m U w W x y Y date fields

These follow strftime(3). On Windows %c and %B are the same value, since the creation time is the birth time.

Escapes

\a \b \f \n \r \t \v \\, \NNN for an octal character code, and \c to stop printing that format immediately and flush.

Windows behavior

-printf is matched against GNU findutils 4.8.0. Where Windows has no equivalent of a Unix concept, winfind reports the closest thing the platform actually knows rather than inventing a number:

Directive On Windows
%p %h The starting point is cleaned, so winfind . prints a.txt, not ./a.txt. This matches what -print has always done.
%m %M Windows has no POSIX permission bits. Files read as 666 (or 444 when read-only) and directories as 777.
%u %g The account name as DOMAIN\name, or the SID when the account cannot be resolved.
%U %G Windows has no numeric uid or gid, so the SID string stands in.
%c Windows has no status-change time. The creation time is reported instead — the same convention MSVC's stat() follows.
%B Genuinely available here, unlike on most Linux file systems.
%i The NTFS file index: unique per volume, and it serves the same purpose as an inode number.
%D The volume serial number rather than a device number.
%b %k %S Taken from the file's allocated size, so they agree with Explorer's "size on disk". Expect smaller numbers than Linux for tiny files: NTFS stores those inside the MFT record instead of allocating a cluster.
%y %Y %l Junctions and directory mount points report as l. Go calls them irregular files, but a junction is a link to a directory.
%Z Always empty; SELinux has no counterpart.
%Tc %Tx %TX %Tr %Tp Fixed C/en_US forms — winfind has no locale support.

Fractional seconds are ten digits wide as in GNU find, but NTFS timestamps have 100 ns resolution, so the last three digits are always zero.

Examples

# Find all Go source files
winfind . -name "*.go"

# Find files larger than 1 MB
winfind C:\src -type f -size +1M

# Find log files modified in the last 7 days and delete them
winfind . -mtime -7 -name "*.log" -delete

# List all files with details
winfind . -type f -ls

# Escape a metacharacter: a file actually named v*.c
winfind . -name "v\*.c"

# Everything under a sub-directory, either separator
winfind . -path "src\cmd\*"

# Find files NOT matching a pattern
winfind . -not -name "*.exe"

# Combine conditions with OR
winfind . -iname "*.jpg" -or -iname "*.png"

# Run a command on each match
winfind . -name "*.txt" -exec notepad {} ;

# Limit search depth
winfind . -maxdepth 2 -type f

# Find empty directories
winfind . -type d -empty

# Newest files first
winfind . -type f -printf "%T@ %p\n" | winsort -rn | wincut -d' ' -f2-

# Size and path, tab separated, largest first
winfind . -type f -printf "%s\t%p\n" | winsort -rn

# A column layout of your own
winfind . -type f -printf "%-40p %8s %TY-%Tm-%Td\n"

# Null-separated paths for xargs -0, via -printf
winfind . -name "*.log" -printf "%p\0"

License

MIT

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages