Scanning
exav prints clamscan’s output format. The flags are exav’s own, and there is a
fourth exit code. This guide covers everyday scanning; see the
CLI reference for every flag.
Targets
Section titled “Targets”exav file.bin # scan one fileexav /var/www # scan a directory, recursivelyexav --no-recursive /var/www # just the directory's own filesexav --quiet /data # only print errors and detectionscat file.zip | exav - # scan stdin (constant memory, any size)exav https://host/obj.zip # scan over HTTP range requests (needs an `http-scan` build)exav -d ./mydb /data # use a signature database (file or dir)A named directory recurses by default. Naming one and getting some of it is the kind of surprise that reads as a clean result: the files that were never opened are indistinguishable, in the output, from files that were and were fine.
All examples assume signatures are loaded — either via -d / --sig-dir, or the
default --sig-dir directory. See Signatures.
Streaming vs seekable
Section titled “Streaming vs seekable”exav has two input modes, and the difference matters for what gets analysed:
- Stream (a forward-only pipe,
exav -): runs the constant-memory pattern + hash core. Unlimited size. Structural unpacking of archives requires seeking, so a pure pipe does pattern+hash only. - Seekable (local files, or
http(s)://URLs): additionally drives archive extraction and full structural analysis.
Scanning over HTTP costs bandwidth, not memory
Section titled “Scanning over HTTP costs bandwidth, not memory”A seekable source may be read more than once. Some checks need a whole object at a time — overlapping ZIP records, overlapping MBR partitions, broken media and executable headers — while extraction walks members one at a time, so the object is read again to run them.
That is deliberate. exav optimises for memory, not bandwidth. The bound it
holds to is the working set: a 4 GB archive is scanned without a 4 GB
allocation, whatever the transfer costs. Over http(s):// this means a range
source is fetched a small, constant number of times rather than once, and an
object under the deep-analysis limit is likely fetched in full.
If you would rather pay in memory or disk than in transfer — metered egress, a slow link, an object store that bills per request — download the object and hand exav the file:
curl -sO https://host/obj.zip && exav obj.zipAn object too large to hold is never held: it is scanned member-by-member, and the whole-object checks are reported as skipped rather than quietly dropped.
Output format
Section titled “Output format”The human-readable output is one line per file:
/path/to/file: Win.Trojan.Agent-1234 FOUND/path/to/clean: OK/path/to/huge.bin: file size 9663676416 exceeds max-scan-size 4294967296; scanned first 4294967296 bytes only LIMITS-EXCEEDED PARTIAL/path/to/secret.zip: encrypted archive member PASSWORD-PROTECTED PARTIALA line that could not be fully examined ends with two words, not one: the
category saying which condition stopped the scan, and
the status PARTIAL that every category shares. The reason comes first.
followed by a clamscan-style summary. --quiet is the single output dial —
it suppresses the OK lines and the summary together, with -v at the other
end of the same dial.
JSON output
Section titled “JSON output”For tooling and pipelines, --json emits newline-delimited JSON — one object
per scanned input, plus a final summary object:
exav --json /data | jq 'select(.status=="FOUND")'status is the word the human line ends with — OK, FOUND, ERROR,
PARTIAL — and it is on every result object. category is not: it
sub-classifies a PARTIAL (LIMITS-EXCEEDED, UNSCANNABLE,
PASSWORD-PROTECTED) and is absent everywhere else, so filtering a pipeline on
it returns nothing at all rather than failing. Select on status:
exav --json /data | jq 'select(.status=="PARTIAL")' # not fully examinedexav --json /data | jq 'select(.status|test("FOUND|PARTIAL"))' # everything uncleanVerbose findings
Section titled “Verbose findings”-v / --verbose prints informational findings (detected file type, entropy,
imphash, ML score) alongside the verdict.
All-match mode
Section titled “All-match mode”By default exav stops at the first matching signature per file. --all-matches
reports every match (what clamscan spells --allmatch):
exav --all-matches /dataFilters
Section titled “Filters”Include/exclude by path regex — repeatable, matching clamscan’s semantics:
exav /data --exclude '\.log$' --exclude-dir 'node_modules' --include '\.exe$'--exclude-dir matches a directory’s own path, which has no trailing
separator — so node_modules skips the directory, while /node_modules/ skips
only what is nested below it and lets files sitting directly inside through.
The looser pattern is the one to write, or anchor it explicitly as
/node_modules($|/).
Exit codes
Section titled “Exit codes”Each code is a status word, and the status is the last word on the result line:
| Code | Status | Meaning |
|---|---|---|
0 |
OK |
Clean — everything fully scanned, no matches. |
1 |
FOUND |
At least one detection. |
2 |
ERROR |
exav could not do its job — an unreadable path, a database that would not load. |
3 |
PARTIAL |
A file couldn’t be fully examined: LIMITS-EXCEEDED / UNSCANNABLE / PASSWORD-PROTECTED. |
3 is the never-silent-clean invariant
made visible: a file exav couldn’t finish examining never counts as a pass, and
it is kept apart from 2 so “the scanner broke” and “this object needs a
decision” are not one signal. --partial-as
folds 3 into any of the others. See
Verdicts & exit codes for the full table.
Encrypted archives
Section titled “Encrypted archives”An encrypted member is reported PASSWORD-PROTECTED, never a silent clean.
Supply passwords to decrypt and scan inside:
exav --passwords secret --passwords hunter2 /data # try a pool, in orderPasswords also load from a ClamAV .pwdb database in the signature directory.
ZIP (ZipCrypto + WinZip AES), encrypted DMG, 7z AES-256, PDF and Office —
legacy XLS (RC4-basic, RC4-CryptoAPI, XOR) and OOXML (AES standard and agile) —
are all decrypted. Office additionally auto-tries Excel’s default
VelvetSweatshop and the empty password, so no --passwords is needed for the
common malware case. RAR AES and PKWARE Strong Encryption are detected but not
decrypted.
Heuristics
Section titled “Heuristics”exav runs a small FP-safe, ClamAV-parity heuristic subset by default
(imphash matching and the PDF obfuscated-name check). --detect heuristics
turns on the exav-exclusive superset (PE entropy, packer detection, TLSH fuzzy
matching, a static ML baseline scorer) — higher FP risk, so it’s opt-in:
exav --detect heuristics -v sample.exe--detect is the one dial for every heuristic detector: all, none, or a
comma-separated list of heuristics, macros, broken, broken-media,
partition-intersection, phishing, packed, pua.