Search Zstandard Files with zstdgrep, No Unpacking
zstdgrep lets you run ordinary grep patterns against .zst files without ever decompressing them to disk yourself. The installed command is zstdgrep from zstd 1.5.6, and its man page describes it as a small wrapper that decompresses through zstdcat before handing off to grep.
The route
Jump straight to the step you need, or tick off Done means at the end.
Allow about ten minutes. You need a shell, the zstdgrep command and read access to the input files. These examples only read data and print matches; they do not need elevated privileges.
1. Check the command before searching
Confirm which executable your shell will run and record its version:
$ command -v zstdgrep
/usr/bin/zstdgrep
$ zstdgrep --version
zstd 1.5.6
The version output comes from the installed zstd tools. Option support belongs to both zstdgrep and the grep implementation it calls, so check the local grep manual when you need a less familiar grep feature.
Checkpoint
If command -v prints nothing, stop and install or enable the zstd package through your normal system-management process. Do not compensate by running an unknown copy from the current directory.
2. Search one compressed file
Put the pattern first and the compressed file afterwards:
$ zstdgrep 'ERROR' /var/log/example.log.zst
2026-09-28T08:14:22Z ERROR connection refused
The pattern and remaining arguments get passed straight to grep. Quoting the pattern is a safe default: it stops the shell treating characters such as *, $ or spaces as shell syntax before grep ever receives them.
zstdgrep never rewrites the input. It reads the compressed stream, decompresses it for the search, and sends matching lines to standard output. If the source is unreadable or not a valid zstd stream, expect an error from zstd rather than a match.
3. Search several files and keep their names
Pass more than one file when the question spans a set of archives:
$ zstdgrep 'timeout' /var/log/app-2026-09-27.log.zst /var/log/app-2026-09-28.log.zst
/var/log/app-2026-09-27.log.zst:2026-09-27T19:03:11Z timeout waiting for database
/var/log/app-2026-09-28.log.zst:2026-09-28T07:42:09Z timeout waiting for database
grep normally prefixes each matching line with the file name once it receives multiple files. Add a grep option that changes that display and the output changes too, so treat the result as text for a human review unless you've deliberately chosen a machine-readable format.
Do not use an unquoted wildcard casually. The shell expands /var/log/app-*.zst before zstdgrep even starts, and a large or unexpected set of files can make a search slow or exceed the command line length limit. Inspect the expansion first when the directory is busy:
$ printf '%s\n' /var/log/app-*.zst
4. Use grep flags through zstdgrep
zstdgrep accepts grep flags before the pattern. -n prints line numbers and -i makes a case-insensitive search:
$ zstdgrep -n -i 'warning' /var/log/example.log.zst
18:2026-09-28T08:14:22Z Warning: retrying request
If the pattern begins with a hyphen, use the documented separator so grep doesn't mistake it for another option:
$ zstdgrep -n -- '-deprecated' /var/log/example.log.zst
There's a more precise form for patterns that need to be supplied as an option. With -e, zstdgrep stops looking for a separate pattern argument and passes the expression straight to grep:
$ zstdgrep -n -e 'failed|refused' /var/log/example.log.zst
The regular-expression language is grep's, not a zstdgrep-specific one. Check man grep before using extended expressions, fixed-string mode or output-control flags in a script.
5. Search standard input
When there's no file argument, zstdgrep reads standard input, which is useful when another command has already produced decompressed text:
$ zstdcat /var/log/example.log.zst | zstdgrep -n 'ERROR'
42:2026-09-28T08:14:22Z ERROR connection refused
For a normal compressed file, prefer giving that file directly to zstdgrep. The pipeline above still earns its place when the input comes from another source or needs an earlier transformation. Do not decompress to a temporary file just to search it unless you have a reason to keep that uncompressed copy.
6. Check the result status before scripting around it
Capture the status immediately if a script needs to distinguish a match from a failed search:
if zstdgrep -q 'ERROR' /var/log/example.log.zst; then
printf '%s\n' 'An error was found'
else
status=$?
case "$status" in
1) printf '%s\n' 'No match, or the pattern/file was missing' ;;
*) printf 'Search failed with status %s\n' "$status" >&2; exit "$status" ;;
esac
fi
On the installed command, a matching search returned 0, while a search with no matches returned 1. The man page also documents status 1 for missing arguments or a missing pattern. A missing input file produced a zstd diagnostic and status 1 in testing, so do not treat every 1 as proof the archive was successfully searched and simply had no match.
The example uses -q so a monitoring check does not print matching lines. Remove -q when you need the lines themselves, and save the status before another command overwrites $?.
Common traps
- zstdgrep is for zstandard-compressed input. A file ending in
.zstis only a naming convention; malformed or differently compressed data will fail. - Do not put the file name before the pattern. The synopsis is
zstdgrep [grep-flags] [--] pattern [files ...]. - Do not assume every grep extension is identical across systems. zstdgrep passes arguments straight to the local grep.
- Do not use
sudoas a routine fix. Use elevated privileges only when the file permissions genuinely require them, and consider whether the resulting log data should be exposed to your account first. - Consider modern grep alternatives such as ripgrep, where zstd support is enabled, for complex searches; their command-line details aren't necessarily the same as zstdgrep's.
Done means
- The local command and zstd version were checked.
- The pattern was quoted and placed before the compressed file names.
- Grep flags were used only where their local grep behaviour was understood.
- Standard input was used deliberately, rather than creating an unnecessary uncompressed copy.
- A script checked the exit status immediately and distinguished no-match from other failures as far as the command permits.
- No input archive was modified.