Unzip and merge multiple zip files (e.g. Google Drive Takeout splits) into a single directory, and automatically fix garbled filenames produced by zips that don't set the UTF-8 flag (CP437 → UTF-8).
Google Drive Takeout, and many Windows-created .zip archives, store filenames
in CP437 instead of UTF-8. When extracted on macOS or Linux, non-ASCII names
(Japanese, Chinese, Korean, accented characters, etc.) come out as mojibake.
This script extracts all zips into one merged directory, then walks the tree
and renames any entries whose names round-trip cleanly from CP437 back to
UTF-8.
Works on Linux, macOS, and Windows (via Git Bash, MSYS2, Cygwin, or
WSL — any environment that gives you bash).
bashunzipiconv(used for the encoding fix; supports CP437)find,awk,df,stat(POSIX, pre-installed everywhere above)
All of these come pre-installed on a default macOS install, every common Linux distro, and inside Git Bash / MSYS2 / Cygwin / WSL on Windows. No Python required.
Run the script straight from GitHub — no clone, no install. cd into the
directory that contains your *.zip files first.
# Extract every *.zip in the current directory into ./merged
curl -fsSL https://raw.githubusercontent.com/raftio/unzip-merge/master/unzip_merge.sh | bash
# Use a custom output directory
curl -fsSL https://raw.githubusercontent.com/raftio/unzip-merge/master/unzip_merge.sh | bash -s -- -o out
# Overwrite existing files in the output directory
curl -fsSL https://raw.githubusercontent.com/raftio/unzip-merge/master/unzip_merge.sh | bash -s -- -f
# Pass specific zips explicitly (instead of globbing *.zip)
curl -fsSL https://raw.githubusercontent.com/raftio/unzip-merge/master/unzip_merge.sh | bash -s -- a.zip b.zip c.zipAnything after
bash -s --is forwarded to the script as arguments.
Or download once and reuse it:
curl -fsSLO https://raw.githubusercontent.com/raftio/unzip-merge/master/unzip_merge.sh
chmod +x unzip_merge.sh
./unzip_merge.sh -o out| Flag | Description |
|---|---|
-o OUTPUT |
Output directory (default: ./merged) |
-f |
Overwrite existing files (unzip -o) |
-h |
Show help |
By default, existing files are kept (unzip -n), so re-running the script is
safe and idempotent.
- Collects the list of zip files (positional args, or
*.zipin the current directory). - Sums their sizes and warns if the destination has less than ~1.2× free space.
- Extracts each zip into the output directory in order.
- Walks the output tree (bottom-up via
find -depth) and usesiconvto tryUTF-8 → CP437 → UTF-8on every entry name; if it round-trips cleanly, renames it — fixing mojibake from archives that lack the UTF-8 flag.
chmod +x unzip_merge.shOptionally drop it on your PATH:
# Linux / macOS
ln -s "$PWD/unzip_merge.sh" /usr/local/bin/unzip-mergeOn Windows, just call it from Git Bash / MSYS2 / WSL:
bash unzip_merge.sh -o mergedA self-contained test suite lives in tests/run_tests.sh. It uses only
bash, zip, unzip, iconv, and find (no test framework, no external
deps).
bash tests/run_tests.sh # run all tests
bash tests/run_tests.sh -v # show output of failing tests
bash tests/run_tests.sh -k encoding # only tests whose name matches "encoding"The suite covers: CLI flags (-h, -o, -f), error paths (no zips, missing
zip), single/multiple zip merging, default skip-existing vs -f overwrite,
explicit zip args, and the CP437→UTF-8 mojibake fix (top-level + nested
directories, plus the no-op case for already-valid UTF-8 names).
MIT