As Mathias said, unzip
has no such option, but a one-liner bash script can do the job.
Problem is: the best approach depends on your archive layout. A solution that assumes a single top-level dir will fail miserably if the content is directly in the archive root (think about /a/foo
/b/foo
/foo
and the chaos of stripping /a
and /b
).
And the same fail happens with tar --strip-component
. There is no one-size-fits-all solution.
So, to strip the root dir, assuming there is one (and only one):
unzip -d "$dest" "$zip" && f=("$dest"/*) && mv "$dest"/*/* "$dest" && rmdir "${f[@]}"
Just make sure second-level files/dirs do not have the same name of the top-level parent (for example, /foo/foo
). But /foo/bar/foo
and /foo/bar/bar
are ok. If they do, or you just want to be safe, you can use a temp dir for extraction:
temp=$(mktemp -d) && unzip -d "$temp" "$zip" && mkdir -p "$dest" &&
mv "$temp"/*/* "$dest" && rmdir "$temp"/* "$temp"
If you're using Bash, you can test if top level is a single dir or not using:
f=("$temp"/*); (( ${#f[@]} == 1 )) && [[ -d "${f[0]}" ]] && echo "Single dir!"
Speaking of Bash, you should turn on dotglob
to include hidden files, and you can wrap everything in a single, handy function:
unzip-strip() (
local zip=$1
local dest=${2:-.}
local temp=$(mktemp -d) && unzip -d "$temp" "$zip" && mkdir -p "$dest" &&
shopt -s dotglob && local f=("$temp"/*) &&
if (( ${#f[@]} == 1 )) && [[ -d "${f[0]}" ]] ; then
mv "$temp"/*/* "$dest"
else
mv "$temp"/* "$dest"
fi && rmdir "$temp"/* "$temp"
)
Now put that in your ~/.bashrc
or ~/.profile
and you'll never have to worry about it again. Simply use as:
unzip-strip sourcefile.zip mysubfolder
(notice it will automatically create mysubfolder
for you if it does not exist)
2Just use bsdtar – drizzt – 2015-08-10T17:01:09.477