Sample output
backup.zip: gzip compressed data, from Unix, original size modulo 2^32 71680
deploy.sh: Bourne-Again shell script, ASCII text executable, with CRLF line terminators
dump.bin: OpenPGP Public Key
export.csv: CSV text
latest: symbolic link to export.csv
logo.png: PNG image data, 16 x 16, 8-bit/color RGBA, non-interlaced
q3-report.pdf: JSON text data
Three of these are lying about themselves: backup.zip is a gzip stream that unzip will refuse and tar -xzf will open, and q3-report.pdf is JSON that no PDF reader will ever render. The line that saves the most time is deploy.sh, because “with CRLF line terminators” is the full explanation for bad interpreter: /bin/bash^M. Note that latest is reported as the link itself rather than as what it points at; file -L latest follows it and says CSV text.
When you would reach for it
A download that arrived with no extension, or with one you have no reason to trust: an export from a vendor, an attachment, an artifact pulled from a build server. It is also the first command to run when a parser rejects a file that looks fine, because the answer is usually that the bytes are not what the name promised, or that the file is text with the wrong line endings or the wrong encoding. Point it at a whole directory and you get a manifest of what you are actually holding.
Gotchas
- The exit status is 0 even when a file cannot be opened.
file missing.txtprintscannot openand still reports success, so a script that checks$?will sail past a typo in a path. Add-Eto turn open failures into real errors and an exit status of 1. - Magic matching is a guess, and it is confident about guesses. The
dump.binline above is 4 KB of output from/dev/urandom, identified as an OpenPGP public key because a few random bytes at the front happened to match.file -k dump.binkeeps going past the first hit and shows every match, which is how you tell a real identification from a collision. - Only the head of the file is examined. The text-versus-binary decision scans the first 65536 bytes, so a log file that is clean text for 64 KB and then turns into garbage still reports
ASCII text. Raise the window withfile -P encoding=1000000when you need certainty about a large file. - Symlinks are not followed by default, and
POSIXLY_CORRECTin the environment silently reverses that, so the same command can answer differently on two machines. Be explicit:-Lto follow,-hto report the link. On Alpine there is nofileat all until you runapk add file, since busybox does not provide it. macOS ships its own build with its own copy of the magic database, so descriptions can differ word for word from Linux even though the flags are the same.
Variants
$ find . -type f | file -f -
Identify every file in a tree. -f - reads the list of names from standard input, one per line, so file starts once instead of once per file
$ file -z backup.zip
Look through the compression and report what is inside: POSIX tar archive (GNU) (gzip compressed data, from Unix), without unpacking anything
$ [ "$(file -b --mime-type upload.dat)" = image/png ] || echo "not a png"
-b drops the filename and --mime-type drops the prose, leaving one token that a script can compare against