27

Depending on how a zip file is created, sometimes it will extract all of the files directly, and sometimes it will extract the files into a subdirectory.

If the latter is true, how can I force the unzip command to "ignore" that first level directory?

Example:

cd /tmp
wget http://omeka.org/files/omeka-1.5.1.zip
mkdir omeka
unzip omeka-1.5.1.zip -d omeka/
cd omeka/
ll

What I'm getting is /tmp/omeka/omeka-1.5.1/:

total 12
drwxr-xr-x 3 root root 4096 2012-05-08 18:44 ./
drwxrwxrwt 6 root root 4096 2012-05-08 18:44 ../
drwxr-xr-x 5 root root 4096 2012-04-20 14:54 omeka-1.5.1/

What I want is of the files extracted to /tmp/omeka/, (one level up and no version number included in the directory structure)

/tmp/omeka/(files)

I know I can use the -j option to "junk paths" but I want to keep the subdirectory structure, just not the top level directory structure. How can I do this?

5
  • ps: I'm aware that I can extract and then mv the files, but I wanted to see if there was a way to do this straight away from the unzip command
    – cwd
    Commented May 8, 2012 at 18:55
  • I'm not aware of one. But if the filename always matches (apart from the extension) the directory name, it's pretty straightforward to script the rename of the extracted dir.
    – Mat
    Commented May 8, 2012 at 19:07
  • 3
    I see what you're trying to do, but I wouldn't recommend it. Stripping the version number from your project sources is a bad idea; you should just extract normally and use a ln -s if you want a shorter name without version information. That way you always know what version you are running and can easily update to a new version by switching the link. Commented May 8, 2012 at 19:38
  • @BurtonSamograd - that would be ideal, but I'm still curious if there is an effective way to do this because some software, take Wordpress as an example, puts every version inside a /wordpress/ directory (no version number) inside the zip file. It is indeed fine to unzip and then mv but having no control of this and having to do it in two steps has always gotten on my nerves a little. Fortunately Wordpress also comes in a .tar.gz flavor :)
    – cwd
    Commented May 9, 2012 at 2:01
  • Try cd /tmp/omeka && ln -s -T . omeka-1.5.1 Commented May 9, 2012 at 22:39

7 Answers 7

9

If your zip file contains no directory structure or you do not need to preserve it, you can use this:

cd /tmp
wget http://omeka.org/files/omeka-1.5.1.zip
unzip -j omeka-1.5.1.zip -d omeka
cd omeka
ll
0
5

Use a FUSE filesystem that allows you to browse archives like directories, such as AVFS. Use cp to extract the files to the directory of your choice.

mountavfs
cp -Rp ~/.avfs/tmp/omeka-1.5.1.zip\#/omeka-1.5.1 omeka

Since we're assuming that there is a single toplevel directory in the archive, you can shorten this to

cp -Rp ~/.avfs/tmp/omeka-1.5.1.zip\#/* omeka
1
  • 1
    @Giles you are the expert who comes and answers all the questions nobody else can answer, so I guess this is truly the only way to do this. I was hoping unzip had some magic to do this, such as specifying the files to be extracted like */*, that I could use without installing another utility, but I guess not. Thanks. +1
    – cwd
    Commented May 9, 2012 at 2:05
4

This script is not robust, but works in the simple cases:

...
dest=omeka
unzip omeka-1.5.1.zip -d $dest/

if [ `ls $dest | wc -l` == 1 ]; then
  subdir=`ls $dest`
  mv $dest/$subdir/* $dest/
  rmdir $dest/$subdir
fi

It just checks to see if there is exactly one subdirectory, and if so, moves everything up out of it then deletes it.

3

Solution: unzip through a temporary symlink

In my case I wanted to overwrite a directory full of existing files with the content of a zip, without extracting the full zip first.

This solution is simple, does not require FUSE, only requires possibility of creating a symlink to the target directory.

How to do it

Assuming you know the extra directory, in you case omeka-1.5.1, you can do this:

    mkdir omeka
    ln -s . omeka/omeka-1.5.1 # create a symlink that redirects output
    unzip omeka-1.5.1.zip -d omeka/
    rm omeka/omeka-1.5.1 # remove symlink
    rmdir omeka

unzip will try to unzip to omeka-1.5.1 which is actually a symlink to containing dir. As a result, files will land in omeka directly.

Possible variants

You can imagine variants to redirect one or more parts of a depeer hierarchy.

    ln -s ../myfoo omeka/omeka-1.5.1/foo
    ln -s ../../mybarxyzzy omeka/omeka-1.5.1/subdir/bar

You can also use this trick if the target filesystem does not allow symlinks:

    cd /tmp # or any dir where you can create a symlink
    mkdir omeka
    ln -s /path/to/target/even/if/no/symlink/possible omeka/omeka-1.5.1
    unzip /path/to/omeka-1.5.1.zip -d omeka/
    rm omeka/omeka-1.5.1 # remove symlink
    rmdir omeka

Conclusion

This solution is kind of a hack but works very well.

Idea for improvement of unzip

It would be desirable to have an option to unzip, like patch --strip=number (or -pnumber) that chops number path components at beginning (ref. Comparing and Merging Files: patch Directories).

5
  • Great solution if you know the path!
    – grepsedawk
    Commented Jun 28, 2019 at 20:00
  • The path can be probed by listing the zip content first, e.g. with unzip -l. This could even be automated using a script that figures out part of the path that is common. Commented Jul 2, 2019 at 7:31
  • Oh totally, it's just a ton easier if you know the path. I played with unzip -l for a little bit but it was a bit more grep/sed/awk than I intended so I backed out.
    – grepsedawk
    Commented Jul 2, 2019 at 13:07
  • zipinfo -1 might be a good start. What is your use case? Commented Jul 3, 2019 at 12:58
  • github.com/grepsedawk/wow-config/blob/… in this case, I know the directory :)
    – grepsedawk
    Commented Jul 3, 2019 at 16:16
2

I've just registered today, so I can't upvote @SteveBennet answer and can't add a comment there.

Based on his answer I created a recursive function like this:

...

shopt -s dotglob # To include hidden files in the move command

function moveSub {
  local dest=$1
  if [ `ls $dest | wc -l` == 1 ]; then
    local subdir=`ls $dest`
    moveSub "$dest/$subdir"
    mv $dest/$subdir/* $dest/
    rmdir $dest/$subdir
  fi
}

moveSub "$dest"

Just like @SteveBennet said: this script is not robust, but works in the simple cases.

Hope it's useful.

1

Most likely you want to run unzip with the -j option, as in:

unzip -j files.zip -d ./output/

But you might be surprised by its behavior:

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

If you extract files.zip in output directory ./output and you get this as a result:

./output/files/subfolder1/f1
./output/files/subfolder1/f2

then the -j option will give you this (all paths striped):

./output/f1
./output/f2
1

Oneliner (for Mac / BSD only)

tar zxvf omeka-1.5.1.zip --strip-components 1 -C omeka/

Note 1: The specified destination folder omeka/ does NOT need to exist beforehand.

Note 2: This only works on Mac / BSD flavor of tar. Unzipping a .zip file with tar will not work on Ubuntu (Linux) or any other unix OS that is not based on BSD.

This can be done on Mac/BSD because bsdtar supports zip file extraction. Note: there are some limitations of the zip file format support that are outside the scope of this answer. Suffice it to say that if you're not trying to stream or pipe the file and the file is not huge, all should be fine.

The following comment by @thorsummoner on a comment on another question provides a little more context:

it almost certainly works by buffering the entire zip file into memory before doing any operations with it. This makes it possible to pretend small zip files are streamable (such as from a trusted http server) but its only skin-deep. zip's aren't and can't be streamable so bsdtar won't solve the problem of mixing big data with zip, nor will it really assist with any problems of working with zip other than maybe licensing as bsdtar (libarchive-tools) is released under the BSD-3-clause-UCB license instead of the infozip license.

5
  • Using tar command for a .zip file extraction didn't work on Ubuntu. I think tar is not made for that
    – Sathesh
    Commented Dec 12, 2023 at 5:53
  • Correct. this is a Mac/BSD only answer, as stated in large text. That means it WILL NOT WORK using the tar command on Ubuntu (Linux) or any other *nix OS that is not based on BSD. Commented Dec 13, 2023 at 13:41
  • Unrelated answers should go as comments as you are not answering the original question. stackoverflow.com/help/privileges/comment
    – Sathesh
    Commented Mar 28 at 16:36
  • Thanks for the tip, but I respectfully disagree. 1) OP doesn't state their host operating system, 2) this answer answers the question for Mac/BSD operating systems, 3) Mac/BSD is a valid flavor of unix, and 4) this stack exchange is for Unix and Linux. I read the link you posted and didn't see anything suggesting my answer should be a comment. Commented Apr 9 at 20:09
  • 1
    I updated the answer to clarify the caveats. Maybe this will help other people. Commented Apr 9 at 20:16

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .