I have a project where I am downloading and compiling a bunch of files. The assumptions I can make about this files are:

  • there will only be one top level directory
  • the folder name doesn't necessarily match the tar.xz/gz filename (in other words something-1.3.5a.tar.gz might extract to a folder called something-1.3.5)

In order to lessen the amount of typing I have to do I wrote a small script, which among other things does the follow:

  • extracts the tar.xz/gz file
  • cds into the directory

My current hack is to do something like this:

test1=$(tar -axvf something-1.3.5a.tar.gz)

cd $(echo $test1 | cut -f1 -d" ")

Basically what this does is it captures the output of the extraction, and takes the first line (which is the top level directory) and then cds into it.

So, my question is this, is there a cleaner/better way of doing this?

  • You could use the t option in tar to list the contents, e.g.: cd "$(tar tfz "$file" | head -n 1 | cut -f1)", but that's not really much better. There's no built-in way to do this (that I know of), as the assumption you make is not always true for every possible tarball. – marinus Sep 14 '15 at 1:53
  • Thanks for your suggestion. Yeah, I had a much cleaner solution but I kept on finding exceptions and they weren't really regular enough to capture in sed and my sed command was just getting longer and longer. Since this solution works, I'm not too concerned if there isn't a better way but I wanted to check just in case there was something obvious that I was missing. – bash_noob-3.14.tar.gz Sep 14 '15 at 2:12
  • If the option -a works for you, you are not using tar but rather gtar. Since there is also no standard tar option -z, in theory the proposal from marinus could fail as well. If you don't like to be portable, you may stay with that... – schily Sep 14 '15 at 8:54
  • Thanks for your input, schily. Since I can always expect to use gtar, I think I should be right. There is no point finding and using a more portable solution if I won't ever need it. – bash_noob-3.14.tar.gz Sep 15 '15 at 0:04
Dir_name=`tar -tzf something-1.3.5a.tar.gz | head -1 | cut -f1 -d"/"`
echo $Dir_name

tar options details,
-t, --list 
-z, --gzip, --ungzip               filter the archive through gzip
-f, --force-local              archive file is local even if has a colon

Here, we are getting the list of file in tar and taking the first line using head -1 cmd, extracting the first field using cut cmd.

  • One thing to note is that if you're doing this with set pipefail, it will error as head has terminated after the first line. – IBam Nov 1 '16 at 15:39

If you have one top directory and nothing, you can simply remember what directories there are before you run tar, and see what you have after you do that, assuming nobody else have changed that directory.

 # ksh terminology: set -A BEFORE .*/ */
 BEFORE=(.*/ */)
 tar -xf blah blah blah
 AFTER=(.*/ */)
 # We can make it O(n), since glob outputs can be assumed to be sorted.
 # Do it.
 for (( i = 0; i < "${#BEFORE[@]}"; i++ )); do
    [ "${BEFORE[i]}" == "${AFTER[i]}" ] || break
 cd "${AFTER[i]}"

This should work with all shells that has arithmetic for, arrays starting with a zero index and with ${#arr[@]} to access array member count.

There is actually one problem, since */ may expand into literally */ where there is no match. In bash (assuming you are using bash because of your tag), you can set shopt -s nullglob for that.

Using */ restricts the globbed items to directories, so you won't worry about files -- Extra files still make it only one dir :)


With a tar.bz2 file, to not get something like:

0               0 2019-04-02 17:20 folder name

I'm using -tf options:

tar -tf file_name.tar.bz2 | head -1 | cut -f1 -d"/"

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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