24

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?

don_crissti
  • 79,330
  • 30
  • 216
  • 245
cwd
  • 44,479
  • 71
  • 146
  • 167
  • 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 May 08 '12 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 May 08 '12 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. – Burton Samograd May 08 '12 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 May 09 '12 at 02:01
  • Try `cd /tmp/omeka && ln -s -T . omeka-1.5.1` – James Youngman May 09 '12 at 22:39

6 Answers6

8

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
Eneko Alonso
  • 189
  • 1
  • 3
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
Gilles 'SO- stop being evil'
  • 807,993
  • 194
  • 1,674
  • 2,175
  • @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 May 09 '12 at 02: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.

Steve Bennett
  • 2,203
  • 6
  • 21
  • 25
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).

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.

elysch
  • 131
  • 1
  • 5
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
Vincenzo Pii
  • 141
  • 5