There are two common ways to do this: using the find command (recommended for its flexibility) or using shell wildcards (globbing).
A typical directory structure might look like this:
project/
├── data1/
│ ├── images.zip
│ └── notes.zip
├── data2/
│ ├── backup.zip
│ └── docs/
│ └── archive.zip
└── scripts/
└── source.zip
You want to extract each .zip file inside its own folder. For example, images.zip should be extracted inside data1/, not in the root project/. unzip all files in subfolders linux
The naive approach—manually unzipping each file—is impractical. Instead, we need a recursive solution that locates all .zip files regardless of depth.
for Loop with findIf you prefer readability and more control inside the loop, use a for loop that processes find results. There are two common ways to do this:
for zipfile in $(find . -name "*.zip"); do
dir=$(dirname "$zipfile")
unzip -o "$zipfile" -d "$dir"
done
Caveat: This breaks if filenames contain spaces or newlines. While rare for .zip files, it's safer to use:
find . -name "*.zip" -print0 | while IFS= read -r -d '' zipfile; do
unzip -o "$zipfile" -d "$(dirname "$zipfile")"
done
-print0 and -d '' handle spaces and special characters correctly..unzipped)find . -name "*.zip" | while read zipfile; do
marker="$zipfile.done"
if [ ! -f "$marker" ]; then
unzip -o "$zipfile" -d "$(dirname "$zipfile")" && touch "$marker"
fi
done
find . -name "*.zip" -exec sh -c 'unzip -o "$0" -d "$(dirname "$0")" && rm "$0"' {} \;
Task: Unzip all .zip files under /data/incoming into folders named after each ZIP (e.g., file.zip → file/). You want to extract each
cd /data/incoming
find . -name "*.zip" -type f -exec sh -c '
base="$0%.zip"
mkdir -p "$base"
unzip -q "$0" -d "$base"
' {} \;
-q makes unzip quiet (suppresses output).A critical distinction in this process is where the extracted files end up.
find command can execute a subshell:
find . -name "*.zip" -exec sh -c 'unzip -d "$1%/*" "$1"' _ {} \;