Equivalent to tar's "--strip-components=1" in unzip?

52

9

I have a script that extracts a tar.gz-file to a specified subdirectory mysubfolder:

mkdir mysubfolder; tar --extract --file=sourcefile.tar.gz --strip-components=1 --directory=mysubfolder;

Is there any equivalent way of doing this with a zip-file?

frigg

Posted 2012-12-12T11:58:17.257

Reputation: 645

2Just use bsdtar – drizzt – 2015-08-10T17:01:09.477

Answers

28

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)

MestreLion

Posted 2012-12-12T11:58:17.257

Reputation: 1 489

This will not unzip into an existing directory structure as I'd hoped (I tried to use . in place of mysubfolder). I ended up just unzipping (unzip zip-with-top-dir.zip) and then copying (cp -rv extracted-top-zip-dir/* .). – catgofire – 2016-10-25T21:14:20.740

4

I couldn’t find such an option in the manual pages for unzip, so I’m afraid this is impossible. :(

However, (depending on the situation) you could work around it. For example, if you’re sure the only top-level directory in the zip file is named foo- followed by a version number, you could do something like this:

cd /tmp
unzip /path/to/file.zip
cd foo-*
cp -r . /path/to/destination/folder

Mathias Bynens

Posted 2012-12-12T11:58:17.257

Reputation: 2 171

Nice approach, but a bit incomplete: you will still have foo* dir with the full extracted content. – MestreLion – 2013-03-28T05:44:14.300

Yes, I didn’t add rm -rf foo-* on purpose as that’s potentially dangerous. What if there already was a folder named foo-bar? Note that the extraction is being done within the /tmp folder, which gets emptied automatically every now and then. – Mathias Bynens – 2013-03-28T10:59:28.097

That's why I chained operations using &&: a given step only happens if the previous step was successful, so the last one (the rm) only runs if all steps completed with no error. – MestreLion – 2013-03-28T15:21:55.407

2That's also why one should never user /tmp/some-hardcoded-folder-name as a temp folder, but instead should use mktemp for that: it guarantees there will be no such existing folder. Check my answer below. – MestreLion – 2013-03-28T15:23:47.663

2

You can use -j to junk paths (do not make directories). This is only recommended for somewhat common single-level archives. Archives with multi level directory structures will be flattened - this might even lead to name clashes for the files to extract.

From the man page of unzip:

   -j     junk  paths.   The  archive's directory structure is not recreated; all files are deposited in the
          extraction directory (by default, the current one).

Pedro Rodrigues

Posted 2012-12-12T11:58:17.257

Reputation: 129