7

I'm trying to make a script that will unzip a password protected file, the password being the name of the file that I will get when unzipping

Eg.

file1.zip contains file2.zip and it's password is file2.

file2.zip contains file3.zip and it's password is file3

How do I unzip file1.zip, and read the name of file2.zip so it can be entered in the script?

Here's a screenshot of what I meant, I just need bash to read that output in order to know the new password (In this case the password is 13811).

Here's what I've done so far

    #!/bin/bash

    echo First zip name:
    read firstfile


    pw=$(zipinfo -1 $firstfile | cut -d. -f1)
    nextfile=$(zipinfo -1 $firstfile)
    unzip -P $pw $firstfile

    rm $firstfile
    nextfile=$firstfile

Now how can I make it do the loop?

molinskeh
  • 73
  • 1
  • 6

2 Answers2

9

If you don't have and cannot install zipinfo for any reason, you can imitate it by using unzip with -Z option. To list the contents of the zip use unzip -Z1:

pw="$(unzip -Z1 file1.zip | cut -f1 -d'.')" 
unzip -P "$pw" file1.zip

Put it to a loop:

zipfile="file1.zip"
while unzip -Z1 "$zipfile" | head -n1 | grep "\.zip$"; do
    next_zipfile="$(unzip -Z1 "$zipfile" | head -n1)"
    unzip -P "${next_zipfile%.*}" "$zipfile"
    zipfile="$next_zipfile"
done

or a recursive function:

unzip_all() {
    zipfile="$1"
    next_zipfile="$(unzip -Z1 "$zipfile" | head -n1)"
    if echo "$next_zipfile" | grep "\.zip$"; then
        unzip -P "${next_zipfile%%.*}" "$zipfile"
        unzip_all "$next_zipfile"
    fi
}
unzip_all "file1.zip"

-Z zipinfo(1) mode. If the first option on the command line is -Z, the remaining options are taken to be zipinfo(1) options. See the appropriate manual page for a description of these options.

-1 : list filenames only, one per line. This option excludes all others; headers, trailers and zipfile comments are never printed. It is intended for use in Unix shell scripts.

pLumo
  • 22,231
  • 2
  • 41
  • 66
  • Awesome it worked, thank you! Btw in the loop function, why did you grep "\.zip$", head -n1 spits out the same output every time with or without grep. Also what does %%.* mean on the 4th line of the loop function? – molinskeh Aug 08 '18 at 13:47
  • 1
    actually `%%.*` should be `%.*`, I changed that. See https://stackoverflow.com/a/965072/7365127 for explanation what it does. – pLumo Aug 08 '18 at 13:52
  • `| grep "\.zip$"` gives same output, but returns true when .zip found and false when not found => As I use it in the `if` or `while` statement, this return value will stop the function when ".zip" is not found anymore. – pLumo Aug 08 '18 at 13:54
8

Ask zipinfo for the filename listed in the zip file, then capture it for the password. Use that password to unzip the file:

pw=$(zipinfo -1 file1.zip | cut -d. -f1)
unzip -P "$pw" file1.zip

Note that the flag to zipinfo is a one not an ell.

Borrowing liberally from Gilles' answer to a similar question, here's a bash loop that will extract a password-protected nested zip file until there are no more zip files:

shopt -s nullglob
while set -- *.zip; [ $# -eq 1 ]
do 
  unzippw "$1" && rm -- "$1"
done

Where I've defined the function unzippw as a wrapper for the zipinfo and unzip commands above:

unzippw ()
{
    local pw=$(zipinfo -1 "$1" | cut -d. -f1)
    unzip -P "$pw" "$1"
}
Jeff Schaller
  • 66,199
  • 35
  • 114
  • 250
  • 1
    +1 for `zipinfo`, didn't know this :-> – pLumo Aug 08 '18 at 12:57
  • It's part of the `unzip` package ([RPM example](https://rpmfind.net/linux/RPM/fedora/28/x86_64/u/unzip-6.0-38.fc28.x86_64.html), [DEB example](https://packages.debian.org/stretch/i386/unzip/filelist)), so I felt safe suggesting it. – Jeff Schaller Aug 08 '18 at 13:09
  • Thanks for the reply, I think I got the logic now, not sure how to continue the loop though – molinskeh Aug 08 '18 at 13:28
  • 1
    That loop will only keep the files extracted at the very end which is probably what OP wants, but it's worth mentioning. – pLumo Aug 08 '18 at 13:56
  • It won’t even keep the last zip file! – Jeff Schaller Aug 08 '18 at 13:57