winfile

command module
v1.1.5 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 13, 2026 License: MIT Imports: 18 Imported by: 0

README

winfile

winfile is a Windows port of the classic Unix file command. It identifies the type of one or more files by examining their content (magic bytes, encoding heuristics, and structural markers) rather than relying on file extensions alone. Output is compatible with GNU file so it drops into existing scripts and pipelines unchanged.

winfile [OPTION...] FILE...

Installation

Pre-built binary — grab the one for your platform from the Releases page and put it on your PATH. The binaries are self-contained, with nothing to extract or install.

Asset Platform
winfile_windows_amd64.exe Windows, x86-64
winfile_windows_arm64.exe Windows, ARM64
winfile_linux_amd64 Linux, x86-64
winfile_linux_arm64 Linux, ARM64

Rename it to winfile.exe (or winfile) once it is on your PATH: the binary takes its name from argv[0], so whatever you call it is what appears in its messages. On Linux, chmod +x it first.

From source (requires Go 1.21+):

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

This builds and places winfile.exe in your GOBIN directory (typically %USERPROFILE%\go\bin), which should already be on your PATH. winfile --version reports the version you installed.

Build locally:

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

winfile --version reports the release the clone is checked out at. -trimpath keeps absolute build paths out of the binary.

Usage

winfile [OPTION...] FILE...
Option Long form Description
-b --brief Omit filename prefix
-i --mime Output MIME type string (e.g. image/png; charset=binary)
--mime-type Output MIME type only
--mime-encoding Output charset / encoding only
--extension Output slash-separated list of valid extensions
-f FILE --files-from FILE Read filenames from FILE (one per line)
-F SEP --separator SEP Use SEP instead of : between filename and result
-L --dereference Follow symbolic links
-n --no-buffer Flush output after each file
-N --no-pad Don't pad filenames to a common width
-0 --print0 NUL-terminate output records (for xargs -0)
-p --preserve-date Preserve file access time
-r --raw Don't translate unprintable characters
-s --special-files Treat special files as ordinary
-z --uncompress Look inside compressed files
-Z --uncompress-noreport Like -z but omit the compression type
-k --keep-going Don't stop at first match (always done)
-e TEST --exclude TEST Exclude a test by name (accepted, no-op)
-v --version Print version and exit
Examples
# Basic identification
winfile photo.jpg report.pdf archive.zip

# MIME types for HTTP content-type scripting
winfile --mime-type *.dll

# Inspect compressed files
winfile -z logs.gz

# Pipe-friendly NUL termination
winfile -0 -b *.exe | xargs -0 -I{} echo "Found: {}"

# Feed a list of paths from another tool
Get-ChildItem -Recurse -Filter *.bin | % FullName | winfile -f -

# Brief + separator for custom formatting
winfile -b -F " => " main.go go.mod

# Encoding detection
winfile --mime-encoding *.txt *.json
Sample output
winfile.exe:   PE32+ executable (x86-64), for MS Windows
report.pdf:    PDF document, version 1.7
photo.jpg:     JPEG image data, Exif standard
archive.zip:   Zip archive data
notes.txt:     UTF-8 Unicode text with CRLF line terminators
empty.dat:     empty
script.py:     Python script text executable
main.go:       Go source text
ntdll.dll:     PE32+ DLL (x86-64), for MS Windows

Detected formats

Binary / structured
Category Formats
Executables PE (EXE/DLL/OBJ), ELF (full detail), Mach-O
Archives ZIP, gzip, bzip2, XZ, zstd, LZ4, lzip, LZMA, 7-Zip, RAR, tar (ustar/GNU), Cabinet, ar
Office (OOXML) DOCX, XLSX, PPTX (detected inside ZIP)
Office (legacy) OLE2 compound document (DOC, XLS, PPT, MSG)
Images PNG, JPEG, GIF, BMP, TIFF, WebP, AVIF, HEIF/HEIC, ICO, CUR, JPEG 2000, PSD, XCF, PPM/PGM/PBM, SVG
Audio WAV, FLAC, Ogg (Vorbis/Opus/Theora/Speex), MP3, MIDI, AIFF, AU, APE, WavPack
Video AVI, MP4/M4V/MOV/3GP, MKV, WebM, FLV, ASF/WMV, MPEG, H.264
Documents PDF, PostScript, RTF
Fonts TTF, OTF, WOFF, WOFF2
Databases SQLite 3, Microsoft Access
Disk images ISO 9660, MBR boot sector, VMDK, VHD, VHDX, QCOW2
Bytecode Java .class, Python .pyc
Crypto PEM, DER (X.509)
Containers EPUB, JAR, APK (detected inside ZIP)

ELF detail

ELF images are described the way GNU file describes them, not just by class and object type:

$ winfile -b /bin/ls
ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=897a5bd7…, for GNU/Linux 3.2.0, stripped

$ winfile -b core.1234
ELF 64-bit LSB core file, x86-64, version 1 (SYSV), SVR4-style, from '/usr/bin/crasher', real uid: 1000, effective uid: 1000, real gid: 1000, effective gid: 1000, execfn: '/usr/bin/crasher', platform: 'x86_64'

Reported: object type, machine, ELF version and OS/ABI; linkage (dynamically / statically / static-pie linked); the PT_INTERP interpreter path; GNU and Go build IDs; the .note.ABI-tag target OS and version; presence of .debug_info; stripped state; and, for core dumps, the dumping process, its real/effective uid and gid, execfn and platform.

MIME output follows the object type the same way:

Object type --mime-type
ET_REL (relocatable) application/x-object
ET_EXEC (executable) application/x-executable
ET_DYN, PIE application/x-pie-executable
ET_DYN, shared library application/x-sharedlib
ET_CORE (core dump) application/x-coredump
anything else application/octet-stream

Two things are inferred rather than read from a field, matching what GNU file does:

  • pie executable vs shared object. Both are ET_DYN. An image whose .dynamic section carries DT_FLAGS_1 with DF_1_PIE is reported as a PIE executable; otherwise it is a shared library.
  • static-pie linked. An image with a .dynamic section but no interpreter and no DT_NEEDED entries has nothing to bind at run time.

Verified against GNU file 5.41 over a 1348-file corpus (/bin, shared libraries, relocatable objects, static and PIE executables, Go binaries and a core dump): no differences in the ELF description, and none in --mime-type, -i or --extension output either.

Known differences from GNU file
Case Behavior
Uncommon e_machine values Only x86-64, i386, ARM, AArch64, RISC-V and 64-bit PowerPC are named (with their flag-derived ABI suffixes). Other machines are left unnamed rather than guessed at, so the description omits the architecture instead of printing *unknown arch 0x…*.
Non-seekable input Reading a compressed payload with -z yields the header-only description — class, byte order, type, machine, version, OS/ABI. GNU file behaves the same way here.
Truncated or malformed images If the ELF structure will not parse, winfile reports the header-only description. GNU file reports as much as it decoded plus an error fragment such as missing section headers at 14408.
e_version other than 1 The header-only description is used, because Go's debug/elf rejects the file. Real toolchains always emit 1.
NetBSD core dumps Reported as NetBSD-style; the NT_NETBSD_CORE_PROCINFO fields (pid, uid, gid, signal) are not decoded.
setuid / setgid prefix Not reported. This comes from the file's permission bits, not from the ELF image, and Windows has no such bits.
Text / source code

Encoding detection: UTF-32 (LE/BE BOM), UTF-16 (LE/BE BOM), UTF-8 (with or without BOM), ASCII. CRLF vs LF line endings are reported.

Source languages detected: Go, Python, JavaScript, TypeScript, JSX/TSX, Java, C, C++, C#, Rust, Ruby, PHP, Perl, Shell (bash/sh/zsh/fish), PowerShell, DOS Batch, Lua, R, Swift, Kotlin, Scala, Haskell, OCaml, Elixir, Erlang, Clojure, Lisp, Zig, Nim, V, D, Fortran.

Markup & data: HTML, XML, SVG, CSS/SCSS, JSON, JSON Lines, YAML, TOML, Markdown, reStructuredText, LaTeX, CSV, TSV, SQL, Protocol Buffers, GraphQL.

Build & tooling: Makefile, CMake, Dockerfile, .gitignore.

Shebang detection: identifies the interpreter from #! lines for scripts with no extension.

Windows-specific notes

The following GNU file options have no Windows equivalent and are not supported:

Option Reason
-m FILE / --magic-file winfile uses compiled-in signatures, not libmagic's database format
-c / --checking-printout no magic file to syntax-check
-l / --list no magic strength table
Block/character device types Windows has no /dev device tree

Everything else — MIME output, compression unwrapping, symlink following, padding, NUL termination, files-from, separator, preserve-date — works exactly as on Linux.

Part of the win* toolkit

winfile is one of several GNU/Unix tool ports for Windows maintained under github.com/fermat-tech:

Tool Equivalent
winegrep egrep
winfind find
winls ls
winwc wc
winwhich which
winless less
winheadtail head / tail
windate date
winsort sort
winfile file

License

MIT — see LICENSE.

Documentation

Overview

detect.go is the top-level file analyser for winfile.

[analyseFile] is the entry point: it stats the path, handles Windows special files (symlinks, directories, named pipes, sockets), opens the file, reads the magic-byte window, and delegates to [doAnalyse].

[doAnalyse] runs the two-pass detection pipeline:

  1. Magic-byte scan via [detectMagic] (magic.go).
  2. Text-encoding scan via [detectText] (text.go) if magic returns nil.

If neither pass succeeds the file is reported as "data" with MIME type application/octet-stream.

Compression unwrapping (-z / -Z flags)

When [options.uncompress] is set and the outer format is gzip or bzip2, [decompressFirst] streams the first magicBufSize bytes of the inner payload and runs the two-pass detection on that buffer. The outer compression type is appended to the description unless [options.uncompressNoreport] is set. XZ is identified at the outer level but inner unwrapping is not supported (no XZ decoder in the Go standard library).

Text refinement

[refineText] narrows a generic ASCII/UTF-8 result to a specific language or format using two signals: JSON structural heuristic first, then the file extension as a secondary hint. The extension table covers ~60 languages and markup formats. A separate base-name table handles extension-less files such as Makefile and Dockerfile.

Access-time preservation (-p flag)

When [options.preserveDate] is set, a deferred os.Chtimes call restores the modification time after the file is read. (Windows does not expose the atime separately via Go's os.Lstat, so mtime is used as the best available approximation.)

elf.go implements GNU file(1)-compatible reporting for ELF binaries.

GNU file describes an ELF image in two halves:

ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, ...
`------------ header fields -------------'  `------ whole-file fields ------'

The header half comes from the first 64 bytes (class, byte order, object type, machine, ELF version, OS/ABI). The rest requires walking the program headers, the section headers and the note records, which are scattered through the whole file.

[elfDetail] therefore has two paths:

  • [elfAnalyse] opens the file with debug/elf and produces the full GNU description. It needs a seekable path.
  • [elfHeaderOnly] uses just the magic-byte window and stops after the OS/ABI. It is the fallback for stdin, pipes and anything debug/elf refuses to parse.

The layout of the description follows file(1) closely enough that the two can be diffed line by line; see the "GNU file compatibility" section of README.md for the deliberate differences.

magic.go implements magic-byte file-type detection.

[detectMagic] reads up to 512+256 bytes from the start of a file and matches them against a table of well-known binary signatures ("magic numbers"). Each entry maps a byte pattern at a fixed offset to a human- readable description, a MIME type string, and a canonical extension.

Format families covered: executables (PE, ELF, Mach-O), archives and compression (ZIP/OOXML, gzip, bzip2, XZ, zstd, LZ4, 7-Zip, RAR, tar, Cabinet), images (PNG, JPEG, GIF, BMP, TIFF, WebP, AVIF/HEIF, ICO, PSD, PPM/PGM/PBM), audio (WAV, FLAC, Ogg/Vorbis/Opus, MP3, MIDI, AIFF), video (AVI, MP4/MOV/M4V, MKV/WebM, FLV, ASF, MPEG), documents (PDF, PostScript, OLE2, RTF), databases (SQLite, Access), bytecode (Java .class, Python .pyc), fonts (TTF, OTF, WOFF, WOFF2), disk images (ISO 9660, MBR, VMDK, VHD/VHDX, QCOW2), and cryptographic containers (PEM, DER).

Format-specific sub-detectors (peDetail, elfDetail, zipDetail, …) inspect additional header fields to produce richer descriptions—e.g. PE files report CPU architecture and EXE-vs-DLL, ZIP containers report OOXML sub-type (DOCX/XLSX/PPTX/JAR/EPUB), and Ogg streams report codec (Vorbis/Opus/Theora/Speex).

winfile is a Windows port of the classic Unix `file` command.

It identifies the type of one or more files by examining their content (magic bytes, encoding heuristics, and structural markers) rather than relying on file extensions alone. Output is compatible with the GNU file command so it can be used as a drop-in replacement in scripts and pipelines on Windows.

Usage:

winfile [OPTION...] FILE...

Common options:

-b              brief output (no filename prefix)
-i              output MIME type string (e.g. image/png; charset=binary)
--mime-type     output MIME type only
--mime-encoding output charset / encoding only
--extension     output slash-separated list of valid extensions
-f FILE         read filenames from FILE (one per line)
-L              follow symbolic links
-z              look inside compressed files (gzip, bzip2)
-Z              like -z but omit the compression wrapper from the description
-0              NUL-terminate output records (for use with xargs -0)

See [usage] for the full option list.

Detection coverage

Binary formats: PE (EXE/DLL), ELF (reported in GNU file's full detail — linkage, interpreter, build ID, ABI tag, stripped state, core-dump provenance; see elf.go), Mach-O, ZIP and OOXML derivatives (DOCX/XLSX/PPTX/JAR/APK/EPUB), gzip, bzip2, XZ, zstd, LZ4, 7-Zip, RAR, tar (POSIX ustar), PNG, JPEG, GIF, BMP, TIFF, WebP, AVIF/HEIF, ICO, PDF, PostScript, OLE2 (legacy Office), SQLite, Java .class, MKV/WebM, MP4/MOV, AVI, FLAC, Ogg (Vorbis/Opus/Theora), WAV, MIDI, TTF/OTF/WOFF/WOFF2, PEM certificates, and many more.

Text formats: ASCII, UTF-8, UTF-16 LE/BE (BOM-detected), UTF-32 LE/BE; Go, Python, JavaScript/TypeScript, JSX/TSX, Java, C/C++, C#, Rust, Ruby, PHP, Perl, Shell, PowerShell, Batch, Lua, HTML, XML, SVG, CSS/SCSS, JSON, YAML, TOML, Markdown, SQL, Dockerfile, Makefile, and more—detected by content first, extension as a secondary hint.

text.go implements text-file encoding detection.

[detectText] scans a byte sample and attempts to classify it as human- readable text, returning the character encoding and a short description compatible with GNU file output.

Detection order:

  1. UTF-32 BOM (FF FE 00 00 / 00 00 FE FF)
  2. UTF-16 BOM (FF FE / FE FF)
  3. UTF-8 BOM (EF BB BF)
  4. Heuristic scan over up to 8 KiB: count NUL bytes, non-UTF-8 sequences, and control characters. Files with >1 % NUL or >5 % invalid bytes are classified as binary (nil return). Otherwise the result is ASCII if no high bytes appear, or UTF-8 otherwise.

CRLF line endings are noted separately in the description when found, to match GNU file behaviour on Windows-style text files.

[looksLikeJSON] is a lightweight check used by the text-refinement stage in detect.go to promote plain-text files to the JSON MIME type when the content starts with '{' or '['.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL