Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

Objective:

After expanding archive a.zip by using command unzip on Ubuntu 12.10, assign the name of the folder a.zip expanded to to bash variable v.

a.zip is assumed and guaranteed to expand to a single folder, rather than multiple folders.

Example:

If unzip a.zip expands to folder ./foo/, echo $v should return foo.

My environment:

GNU/Linux (kernel 3.5.0-25-generic)
Ubuntu 12.10    
bash v4.2.37(1)-release
UnZip 6.00 of 20 April 2009, by Debian. Original by Info-ZIP.

Current workaround I am unhappy with: expand via unzip -d bar a.zip then v=$(ls -1 bar) gives the right result, but messes up the filesystem.

share|improve this question

4 Answers 4

up vote 2 down vote accepted

Here you go:

v=$(unzip a.zip | grep -m1 'creating:' | cut -d' ' -f5-)

Explanation

  • $(...) is a capture shell. It captures the output of the commands within it and substitutes them like a variable.
  • The output of the unzip command is piped to grep, which is passed the argument -m1 to make it return only the first result and the text creating:, which is part of the text unzip prints to stdout when it creates a directory. grep prints creating: foo/ to stdout, which is piped cut.
  • cut is used to print all columns (separated by spaces) after the second one. We specify from 5 until the end of the line though because the word creating: is prefixed with a few spaces. We want to use all of the columns until the end of the line in case the directory name has spaces in it. The final output is foo/.
share|improve this answer
    
Thanks! :) I like the -m1 on grep and I did not know about cut printing to the end via -fN- (omitting last field number). You taught me something! For this -> upvote! Have you not used just one tool (e.g. awk) because 1. grep and cut are more widespread than awk and 2. you find what you wrote more "readable"? I'd like to hear your rationale, since one could concoct a one-liner, like so:stackoverflow.com/questions/15776352 PS: you don't need the quotes, no word-splitting in bash at variable assignment time. v=$(echo a b) is the same as v="$(echo a b)" –  Robottinosino Apr 4 '13 at 0:26
    
It is easier to select all of the fields until the end of the line with cut than with it is with awk. It's also a bit more readable in my opinion. That one-liner is interesting. I'll have to read more about awk. Using quotation marks in variable assignments is a bad habit of mine that resulted from a text editor that colored the text inside of quotation marks nicely. Also, my default language is C. –  paraxor Apr 4 '13 at 13:53
  • You can use unzip -l to list the archive's content and parse the output
  • Or you can list the directory, extract the arcive that list the directory again and compare the two listings using diff
share|improve this answer
    
Evan's solution is more direct: extracts and parses out the dir name.. (although your idea is good, and the basis for Evan's solution.. for this - I am upvoting with thanks..) –  Robottinosino Apr 4 '13 at 0:27

The AVFS filesystem presents a view of the filesystem where every archive file /path/to/foo.zip is accessible as a directory ~/.avfs/path/to/foo.zip#.

mountavfs
ls ~/.avfs$PWD/foo.zip\#

If that ls command shows a single directory, then unzipping the zip file will create that directory and create files underneath it. Do note that a zip file may or may not be organized this way.

Here's a snippet that checks your assumption, and sets dir to the directory name if applicable. Warning: if the zip file contains files whose name begins with a . (dot files), they are ignored.

zip_directory_name () {
  case $1 in
    /*) ;;
    *) set "$PWD/$1";;
  esac
  cd ~/.avfs${1}\# || exit 2
  set -- *
  if [ $# -eq 1 ] && [ -d "$1" ]; then
    printf %s "$1"
  else
    return 1 # more than one top-level file, or a single non-directory
  fi
}
dir=$(zip_directory_name foo.zip) || exit $?
share|improve this answer

Be unhappy with your workaround if you like, but it's actually the correct solution.

d="`mktemp -d`"
unzip -d "$d" "$zipfile"
v="`basename "$d"/*`"
mv "$d"/"$v" "$v"
rmdir "$d"
echo "$v"

This is verbose, but - as long as there really is only one dir in the zipfile, which you should probably check first - it's reliable.

ls and grep is less reliable because it depends on how output looks like. In a different locale, there may not be any "creating:" string. In the next version of unzip, it could be changed for whatever reasons.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.