Batch Compressing Folders in Linux
Imagine you just migrated a stack of site directories to a new machine. Under /var/www sit a dozen project folders — blog, shop, crm, docs — and each one needs to be archived into a compressed package, or bundled together to hand off to a teammate. Running zip -r by hand for each one is not realistic, and that is exactly when loops and pipelines earn their keep. This article covers several practical ways to batch-compress folders in Linux, split into "one archive per folder" and "everything into one archive".
Two approaches at a glance
| Need | Tool | Output | Best for |
|---|---|---|---|
| Each folder as its own archive | zip + for loop |
xxx.zip |
Archiving, download mirrors, per-directory distribution |
| Many folders merged into one archive | tar |
xxx.tar.gz |
Whole-site migration, backups, sharing with colleagues |
| Only folders with a given prefix | zip + wildcard / find |
same-named zip |
Processing a subset of directories |
The division of labour is clear: zip suits "compress each directory on its own", while tar suits "keep the directory structure and package everything together". If zip is not installed, install it with apt install zip on Debian/Ubuntu or yum install zip on CentOS/RHEL.
Method 1: Compress every folder in the current directory to a same-named zip
for i in `ls -1`; do zip -r $i.zip $i; done
In Bash this produces a zip named after each entry: blog becomes blog.zip, shop becomes shop.zip. The idea is straightforward, but there are two traps:
ls -1also lists regular files, so if you only meant to compress folders, you end up with a pile of unexpected archives mixed in;- Names containing spaces or special characters get split on whitespace, producing broken archives or wrong filenames.
A more robust version uses a glob that only matches directories, with nullglob enabled so the loop does not error when nothing matches:
shopt -s nullglob
for d in */; do zip -r "${d%/}.zip" "$d"; done
*/ matches directories only, ${d%/} strips the trailing slash, and the quotes keep names with spaces intact. On a directory with 30 subfolders totalling about 40GB, a single-threaded zip can take anywhere from a few minutes to well over ten, depending on disk and CPU; if speed matters, switch to 7z a -t7z or parallel compression with pigz.
Method 2: Package multiple folders into a single archive
tar -zcvf backup-2026-08-07.tar.gz blog shop docs
-z compresses with gzip, -c creates a new archive, -v prints each file as it is processed, and -f names the output. Unlike the zip loop, tar tucks several directories into one archive while preserving relative paths, which is ideal for whole-site migration or date-based archiving:
tar -zcvf /backup/www-$(date +%F).tar.gz -C /var/www blog shop docs
The -C flag changes directory before packing, so the archive does not carry the leading /var/www path segment and can be extracted anywhere more cleanly. To pull out just one directory later, run tar -xzf backup.tar.gz blog.
Method 3: Compress only folders with a given prefix
The most common batch scenario is "handle only part of the tree". For example, to compress directories starting with ab:
for i in ab*; do zip -r "$i.zip" "$i"; done
Or combine both conditions — "is a directory AND matches the name" — in a single find command:
find . -maxdepth 1 -type d -name "ab*" -exec zip -r {}.zip {} \;
-maxdepth 1 limits the search to the current level so subdirectories are not re-archived; -exec ... {} runs zip once per matched directory. This form is more flexible than a glob when you have many directories or need to exclude certain names.
A practical script: dated batch archiving with a log
Combine the approaches above into a small, repeatable script with a date stamp, then drop it into crontab for scheduled archiving:
#!/bin/bash
# Batch archive every subdirectory under /var/www to /backup, dated, with a log
shopt -s nullglob
out=/backup/archive-$(date +%F)
mkdir -p "$out"
log=$out/archive.log
for d in /var/www/*/; do
name=${d%/}; name=${name##*/}
tar -zcf "$out/$name.tar.gz" -C /var/www "$name"
echo "$(date +%T) done $name" >> "$log"
done
echo "total $(ls "$out"/*.tar.gz 2>/dev/null | wc -l) archives" >> "$log"
Two notes: ${name##*/} extracts the plain directory name from the full path, and 2>/dev/null suppresses the ls error when the output directory is empty. The tar -zcf drops -v so the log does not get flooded; on the first run, keep -v and watch the output once to confirm nothing is missing. Once the script is wired into crontab, combined with the server backup strategy, it gives you a closed loop of "auto-archive + periodic cleanup", with a log to trace which run failed and when.
Frequently asked questions
- Permissions or timestamps lost after extraction: zip does not preserve Unix permission bits by default; prefer
tarfor archival use. - Symlinks copied as their targets:
zipfollows symlinks by default; pass-yto keep the links themselves. - How to verify the result: validate archives with
unzip -t blog.ziportar -tzf backup.tar.gz | wc -l; for archival jobs, wrap the run in a script and record the checksum. - Disk space: when the archives and the source live on the same disk, leave at least as much free space as the source size, or the job may fill the partition halfway through.
- Compression ratio too low: gzip defaults to compression level 6; for a smaller result, request level 9 explicitly (e.g.
tar -zcf --use-compress-program='gzip -9'), or switch toxzwith parallel cores to trade time for size.
Batch compression is a routine file-management task. Combined with the cloud servers category, such as server operations tips and Linux server basics, it forms a complete daily operations workflow. For the symmetric operation of bulk extraction, see bulk unzip on CentOS.
Reference: the
man zipandman tarpages cover both tools, and the GNU tar manual is at https://www.gnu.org/software/tar/manual/.
Original post: https://www.cnblogs.com/cqzhuomi/articles/17284350.html (cnblogs.com CQZHUOMI, repost)