zcat Command: Tutorial & Examples
Read a gzipped file straight to your screen — no unpacking, no temp file, no mess.
What It Is
zcat is cat for files that have been squashed with gzip. You point it at a .gz file and it prints the original contents to your terminal — decompressing on the fly, in memory, never writing the unpacked version to disk. That last part is the whole point, and it's why anyone who reads server logs ends up using zcat constantly: rotated logs are almost always gzipped, and zcat lets you read yesterday's auth.log.3.gz without first turning it back into a file you'd have to remember to delete.
If you've never run a server, here's the situation you're about to walk into. Logs don't live forever in one giant file — a tool called logrotate chops them up nightly and compresses the old pieces to save space, so a real /var/log looks like nginx.log, nginx.log.1, then nginx.log.2.gz, nginx.log.3.gz, and on back through the week. The newest log is plain text you can grep; everything older is a .gz. The wrong instinct — the one everybody has the first time — is to gunzip the file, read it, and unzip a litter of decompressed copies all over your disk. zcat is the right instinct: look without unpacking. By the end of this page you'll read compressed logs as fluently as plain ones, you'll know every flag, and you'll understand a genuinely lovely thing about how gzip files are built that makes all of this possible.
Your First Look
You have a compressed log. Read it:
zcat /var/log/nginx/access.log.2.gz
2026-06-03T08:14:02 INFO nginx started, pid 4821
2026-06-03T08:14:09 WARN upstream timed out (110: Connection timed out)
2026-06-03T08:15:31 ERROR 500 GET /api/checkout from 203.0.113.7
That's it — the file's real contents, streamed to your screen, while the .gz on disk sits there untouched and still compressed. Nothing was written, nothing was left behind to clean up. The file you read and the file on disk are the same compressed object; zcat just unwrapped a copy in flight on its way to your terminal.
For a big log that's a problem (you don't want a million lines scrolling past), so in practice zcat is almost always the front of a pipe — you decompress and immediately hand the stream to something that filters it:
zcat access.log.2.gz | grep ERROR | tail
That pattern — zcat opens the hose, a real tool drinks from it — is 90% of how you'll use it.
How I Use It
Here's the actual sequence in my head when something broke "sometime last week" and the evidence is buried in compressed logs.
First, I figure out which file the timestamp lives in — without decompressing anything. zcat -l lists a .gz file's size and (with -v) its embedded timestamp, so I can find the right rotation before I read a single line:
zcat -lv access.log.*.gz
Second, I read or search just that one file. If I know roughly what I'm hunting, I skip zcat entirely and reach for zgrep — it greps inside the compressed file, no pipe needed:
zgrep "203.0.113.7" access.log.2.gz
Third, when the incident might straddle a rotation boundary, I read several at once. zcat happily takes a list of files and concatenates them into one continuous stream — which is the thing that makes "search the whole week" a one-liner:
zcat access.log.*.gz | grep ERROR
And the move I reach for when a log is enormous: I don't read it, I pipe it to a pager that understands compression directly. zless opens a .gz in less — scroll, search with /, page around — all without ever decompressing the file to disk:
zless huge.log.7.gz
That's the craft: -lv to find the file, zgrep to search it, zcat | ... to stream it, zless to read it. Four tools, one idea — touch the compressed file in place, never unpack it.
The Magic: You're Reading a File That Isn't There
Stop and appreciate what's actually happening, because it's the kind of thing that makes Unix click. A .gz file is not your data — it's a recipe for reconstructing your data. The bytes on disk are Lempel-Ziv codes (gzip uses LZ77), which is to say "go back 240 bytes and copy the next 11" repeated thousands of times. zcat runs that recipe and what falls out the end is your original text — but the original text never exists as a file anywhere. It's assembled byte by byte in a buffer, shoved down the pipe, and forgotten. You are reading a document that, on disk, doesn't exist in readable form at all.
That's not a parlor trick — it's the difference between a server you can work on and one that fills up. A week of gzipped logs might be 200 MB compressed and 2 GB raw. gunzip them all to read them and you've just dropped 2 GB onto a disk that was compressed specifically because it was getting full — a classic way to turn a small investigation into a disk full outage. zcat reads the same 2 GB and writes zero. The data exists only for the instant it's flowing past your eyes.
Pro Tip
When a log is too big to scroll, never
gunzipit — pipe it.zcat big.gz | grep patternorzless big.gzreads the whole thing while writing nothing to disk. The compressed file getting decompressed onto an already-tight disk is how a routine log-dig becomes an incident.
The Flags, Explained
zcat's own flag set is short — and there's a beautiful reason for that we'll get to. Here's every one:
-f,--force— decompress even if the input isn't really compressed, or comes from a terminal. The handy side effect:zcat -fwill happilycata plain file too, so a script that mixes.gzand uncompressed logs can use one command for both. (zcatwithout-frefuses to spew binary garbage at your terminal if you accidentally point it at a non-gzip file — a small kindness.)-l,--list— don't decompress; just print the compressed size, uncompressed size, ratio, and original name. The cheapest possible peek.-v,--verbose— with-l, adds the compression method, CRC, and the embedded timestamp — gold for finding which rotation covers the moment you care about. On its own, narrates what it's doing.-t,--test— check integrity without printing anything. Exit code0means the file decompresses cleanly; non-zero means it's corrupt. The way you verify a backup actually survived.-q,--quiet— suppress warnings.-r,--recursive— walk down into directories, decompressing every.gzit finds.-S,--suffix=SUF— treatSUFas the compressed extension instead of.gz(for oddballs ending in.zor a custom suffix).--synchronous— flush each write to disk before continuing (safer across a crash, slower).-h,--help/-V,--version— the usual.
Your First Look at the List Flag
-l and -lv are worth seeing for real, because they're how you avoid decompressing the wrong file. Plain -l:
zcat -l access.log.2.gz
compressed uncompressed ratio uncompressed_name
176 188 25.0% access.log
And -lv, the version I actually use, with method, CRC, and the original timestamp:
zcat -lv access.log.2.gz
method crc date time compressed uncompressed ratio uncompressed_name
defla 4e64542d Jun 3 23:53 176 188 25.0% access.log
defla is deflate, the LZ77-plus-Huffman algorithm at the heart of gzip (and of ZIP, and PNG, and your TLS connections — it's everywhere). The crc is a checksum baked into the file so corruption can't pass silently; -t is what verifies it. And 25.0% ratio is small only because this demo file is tiny — real text logs compress by 60–70%, which is exactly why rotation gzips them.
Reading It by Example
Build the instinct — readout to verdict.
zcat: foo.gz: not in gzip format → you pointed it at something that isn't gzipped. Maybe it's the current (uncompressed) log — just use cat or grep — or maybe it's compressed with a different tool (.bz2, .xz, .zst), which needs a different reader (see See Also).
zcat -t backup.sql.gz returns exit 0 and prints nothing → the file is intact, end to end. Silence is success. This is how you confirm a compressed backup is actually restorable before the night you need it.
zcat -t old.gz prints unexpected end of file → the file was truncated, almost always because the disk filled up while it was being written. The first half decompresses fine and then it just stops. A partial gzip is worse than no gzip, because it looks like a backup right up until you need it.
zcat a.log.*.gz reads them in the wrong order → the shell expands *.gz lexically, not chronologically, so you get this:
auth.log.1.gz
auth.log.10.gz
auth.log.2.gz
auth.log.10.gz sorts before .2.gz because 1 comes before 2 character by character. For grepping it rarely matters (you're filtering, not reading top to bottom), but if you're reconstructing a timeline, sort numerically with ls -v or sort -V first.
The Whole Z-Family
zcat rarely travels alone. Every common text tool has a z-prefixed cousin that does the same job through gzip, so you never decompress to disk:
zcat—catfor.gz. The hose.zless/zmore— page through a compressed file (zlessis the one you want — fulllesswith search).zgrep—grepinside compressed files.zgrep ERROR *.log.*.gzis the single most useful one for logs.zegrep/zfgrep— the-Eand-Fgrep variants.zdiff/zcmp—difftwo compressed files without unpacking either.
Note
This whole family is built for
gzipspecifically — thezis thegzipz. For files compressed withxzthere'sxzcat/xzgrep/xzless; forbzip2there'sbzcat/bzgrep; for the modernzstdthere'szstdcat/zstdgrep. Same idea, different engine. Logs are overwhelminglygzip, so thez*set is the one to keep in your fingers.
Under the Hood: zcat Is a Tiny Script
Here's a delight that explains why zcat has so few flags of its own. zcat isn't a compiled program — it's a six-line shell script, and its last line is simply:
exec gzip -cd "$@"
That's the entire tool. zcat is a polite alias for gzip -c (write to stdout) -d (decompress). All the heavy lifting — the LZ77 decoding, the CRC check, the streaming — lives in gzip itself; zcat just hands it the right two switches. The same is true of zgrep and zless: they're shell scripts that pipe gzip -cd into the real grep or less. So when you learn zcat, you've really learned a convention — "the z-tool decompresses, then runs the normal tool" — and that one idea unlocks the whole family at once. (It also means zcat and gunzip -c are byte-for-byte identical; they're two doors into the same room.)
The Trick That Makes "Read Many at Once" Possible
Now the genuinely cool thing, the one worth carrying with you. Run this and watch:
cat part1.gz part2.gz > joined.gz
zcat joined.gz
part one
part two
You physically glued two compressed files together with plain cat — concatenating their raw bytes, mid-stream, with no respect for where one ended and the next began — and zcat read the result as one clean document. That should feel illegal. With almost any other file format, stapling two files together byte-wise gives you corruption. But the gzip format is defined as a sequence of members, and a decompressor is required to read member after member until the bytes run out. So gzip files are freely concatenable: glue any number end to end and the whole thing is still a valid gzip file.
This isn't a curiosity — it's load-bearing. It's exactly why zcat a.gz b.gz c.gz works as a single continuous stream (each file is just another member), and it's why log rotation can append compressed chunks without ever re-reading the archive. Every .gz starts with the magic bytes 1f 8b (run od -An -tx1 -N2 file.gz and you'll see them) so a reader can always find where the next member begins. One format decision, made decades ago, quietly gives you "search my whole week of logs in one command" for free.
Gotchas
- It only reads
gzip— not ZIP, not tar. A.gzholds one compressed file. The.tar.gzyou download is a tarball then gzipped;zcatwould hand you the raw tar bytes (gibberish on a terminal). For those, usetar xzfortar tzfto list.zcatshines on single streams: logs, SQL dumps, a.gz'd config. - Wildcards sort lexically.
*.gzgives you.1,.10,.2— covered above. Usesort -Vwhen order matters. - Don't
zcat huge.gzbare. Without a pipe or pager you'll firehose a gigabyte of text past your scrollback. Always end in a| grep, a| less, or just usezless. - A truncated
.gzreads fine until it doesn't. It decompresses the valid prefix, then errors.zcat -tis how you catch this before you trust a backup. - Reaching for plain
caton a.gzprints binary noise — and can leave your terminal in a weird state. If that happens, blind-typeresetand press Enter.
History & Philosophy
gzip was written in 1992 by Jean-loup Gailly and Mark Adler for the GNU project, specifically to dodge the patents that then encumbered the older compress tool's LZW algorithm — a free replacement for a format the free world couldn't legally ship. It won so completely that "the old .Z files" are now a museum piece, and gzip's deflate format became one of the most widely deployed pieces of code on earth: it's inside ZIP, PNG, HTTP's Content-Encoding: gzip, your git packfiles, and the .gz logs we've been reading all page. Mark Adler went on to co-write zlib, the library that does deflate for basically everything — there is a decent chance deflate is running on whatever device you're reading this on right now.
And the z prefix has a lovely lineage worth knowing. The original was zcat — but it came from the pre-gzip compress era, paired with .Z files, and the z stuck around as the universal mark of "this tool speaks compression." When gzip arrived it inherited the convention, then spawned the whole zgrep/zless/zdiff cast — and decades later the modern compressors copied the very same naming, which is why xzcat and zstdcat feel instantly familiar. A single letter, carried forward across thirty years and four different algorithms, all whispering the same promise: I'll unwrap it for you, and I won't make a mess.
Cheat Sheet
zcat file.gz— print a compressed file to stdoutzcat a.gz b.gz— concatenate several into one streamzcat file.gz | grep X— the everyday pattern; decompress then filterzcat -l file.gz— size & ratio without decompressingzcat -lv file.gz— add method, CRC, and original timestamp (find the right rotation)zcat -t file.gz— test integrity; exit0= cleanzcat -f file— also reads plain (non-gzip) fileszgreppattern *.gz— search inside compressed files directlyzlessfile.gz— page/scroll/search a.gzinlesssort -Vyour*.gzlist when chronological order matters
See Also
gzip— the compressorzcatunwraps (andgunzip -cis its twin)zgrep— grep straight into compressed files, no pipe neededzless— page and search a.gzin lesszdiff— diff two compressed files without unpackingcat— the plain-text originalzcatis named aftergrep— what you almost always pipezcatintotar— for.tar.gzarchives, whichzcatalone can't unpack- logrotate — the tool that gzips your old logs in the first place
- disk full — what
gunzip-ing logs to read them can cause
Digging through a week of gzipped logs at 2am to find when it broke?
CleverUptime watches the symptoms hiding in those rotated logs — services flapping, errors climbing, a disk quietly filling — and flags the underlying cause in plain language, so you're not piping
zcatintogrepacross seven files hoping to spot the moment it all went wrong.Want to see your own server's health right now? One command, no signup, no install.