2

I have a tar file that has been tar-ed many times. It follows a similar structure like this:

1000.tar
  |
   --- filler.txt (random text)
       999.tar
         |
          --- filler.txt
              998.tar

                ...

How can I write a command/piece of code to get the final .txt of the tar structure? I can do this by hand tar -xf 1000.tar, but it's more efficient to use code.

I'm thinking the code should go like this:

for i in range 1000, 1:
   tar -xf string(i) + ".tar"
  • Extract to the current directory and use seq or brace expansion. Btw, is this from some sort of challenge? – Panki Oct 7 '19 at 14:40
  • 1
    @Kusalananda Matryoshka ;) Seen that in "hacking" competitions. Our Kali friends... – Rui F Ribeiro Oct 7 '19 at 15:04
  • 1
    @RuiFRibeiro As if hacking had anything to do with extracting obnoxious tar archives... – Kusalananda Oct 7 '19 at 15:06
  • 2
    @RuiFRibeiro its supposedly "forensics" – Amin Karic Oct 7 '19 at 15:13
  • 1
    @Kusalananda That is a common and simple challenge as part of a hacking contest, be it in hackerank, or security meetings. This one is more convoluted: dos.sh/blog/matryoshka ; but you are supposed to do them by yourself and not ask U&L, that is why is a challenge. – Rui F Ribeiro Oct 7 '19 at 15:13
5

This simple bash for loop should do what you need:

for i in {1000..1}; do tar -xf $i.tar; done
  • Ohh, thanks so much! Btw, my directory has a size quota, but i can just make it so it deletes the original tar after. – Amin Karic Oct 7 '19 at 14:48
  • You don't have to extract the archive at all if you know what to look for. The archives are not compressed, so the files inside it would be (mostly) readable. – Kusalananda Oct 7 '19 at 15:00
  • @Kusalananda how? I thought of tar --list, but I don't think that can recurse into sub-tars. How would you get to the final file without untarring the parents? – terdon Oct 7 '19 at 15:06
  • @terdon Create a tar archive of a text file, then look at it in an editor or something (strings maybe). If you know what you're looking for, all you need is a simple awk program to get the last text in the archive of archives. – Kusalananda Oct 7 '19 at 15:09
  • 1
    @Kusalananda Nice! I had no idea. Just posted an answer with that approach, hope you don't mind. – terdon Oct 7 '19 at 15:28
4

Here's a recursive approach that doesn't require you to know the filenames of the tarballs in advance. Put the file in a directory that contains nothing else. Then, assuming all the tarballs contained in the parent archive have a .tar extension, you can simply do:

file=(*tar); while [[ -e $file ]]; do tar xf "$file"; rm "$file"; file=(*tar); done

Explanation

  • file=(*tar); : set the variable $file to contain the file name. There should be only one file that matches the *tar glob in the directory you run this in.
  • while [[ -e $file ]]; do : while $file exists...
  • tar xf $file; rm "$file"; file=(*tar); : untar the current value of $file, then delete the tarball you just extracted, and finally set the $file variable to the name of the new, now only, tar file in the directory.

And here's an even more direct approach (thanks @kusalananda!). I made the tar archive using these commands:

$ cat file 
This is the text!
$ file=file; for i in {1..1000}; do tar cf $i.tar $file; file=$i.tar; done

So this is a file called file which contains the text This is the text!\n. The file has been tarred 1000 times and we now have 1000.tar. We can print the original text out with:

$ awk -F'\0' '/[^\0]/{print $(NF)}' 1000.tar 
This is the text!

Note that that isn't actually the original text. The original newline was eaten by tar but replaced by awk. To get the real value from the archive, you'd need (the last $ there is my prompt, there was no newline):

$ awk -F'\0' '/[^\0]/{printf "%s", $NF}' 1000.tar 
This is the text!$

In this particular case, I was able to recreate the file name as well by telling awk to print the last field ($NF), and the 438th field before the last one:

awk -F'\0' '/[^\0]/{print $(NF) >$(NF-438)}' 1000.tar 

That created a new file with the contents of the original. So the same thing as extracting. However, I don't know if the -428 is a magic number. tar seems to add multiple NULLs in its archive, so I found that by running:

$ awk -F'\0' '{for(i=1;i<=NF;i++){ if($i ~ /file/){print i,NF-i}}}' 1000.tar 
434674 438

That told me that field 434674 had the contents file and that is 438 fields before the final one.

If your awk supports gsub, you can probably make it more general with:

awk -F'\0' '/[^\0]/{gsub(/\0+/,"\0"); print $NF > $(NF-11)}' 1000.tar 

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.