22

Given a (really long) list of zip files, how can you tell the size of them once uncompressed?

36

You can do that using unzip -Zt zipname which prints a summary directly about the archive content, with total size. Here is an example on its output:

unzip -Zt a.zip
1 file, 14956 bytes uncompressed, 3524 bytes compressed:  76.4%

Then, using awk, you can extract the number of bytes:

unzip -Zt a.zip | awk '{print $3}'
14956

Finally, put it in a for loop as in Tom's answer:

total=0
for file in *.zip; do # or whichever files you want
    (( total += $(unzip -Zt $file |awk '{ print $3 }') ))
done
echo $total
19

If you type unzip -l <zipfile>, it prints a listing of files within the zip, with their uncompressed sizes, then the total uncompressed size of all of them.

This is human-readable output, but you can get a machine-readable number using unzip -l <zipfile> | tail -n1 | awk '{ print $1 }'.

To get a total size:

total=0
for file in *.zip; do # or whichever files you want
    (( total += $(unzip -l $file | tail -n1 | awk '{ print $1 }') ))
done
echo $total
15

unzip -l lists the size of each file and prints a final line with their sum. So you can loop through the zip files and add up the output of unzip -l "$zip" | awk 'END {print $1}' or of unzip -Zt "$zip" | awk 'END {print $3}'. For a shell loop, unzip -Zt may be a little faster:

total=0
for z in *.zip; do
  set $(unzip -Zt -- "$z")
  total=$((total + $3))
done

That only tells you the total size of the files. Each file has a small overhead: the space to store its name, the space to store some of its metadata, and possibly a bit of unused space because most filesystems allocate files in blocks. On typical filesystems, the overhead can be up to a few kilobytes. It isn't exactly predictable because the overhead depends on the file size, on the directory structure (because of the directory overhead), and on the filesystem's capabilities to merge multiple small files in the same block.

If most the files are more than a few kilobytes, don't worry about this. But if the files are very small, you might want to take the overhead into account. Once again, the overhead depends on the filesystem. On ext4, each file fills a full block (4kB by default on most systems). The following script approximates the total size by rounding each file up to 4kB and adding the length of the file name plus a few bytes.

for z in *.zip; do
  unzip -l -- "$z"
done | awk '
    $2 ~ /^[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]$/ {total += ($1+4095)/4096*4096 + length($0)}
    END {print total}
'
  • +1 for mentioning small files and the fact that filesystems don't pack small files together the way a zip does. AFAIK, no mainstream filesystems on win/OSX/Linux/BSD (i.e. ones you could recommend that someone use for / and /home on their desktop or server) have small file packing as an option. Reiserfs had an option to do this this (and tails of larger files), but it's unmaintained. I hadn't thought of massively long file names, though. Good catch. – Peter Cordes Sep 17 '15 at 2:39
  • You might also add in a constant 256B or 512B per file, as that's the size of an inode (on XFS). I think ext4 still statically allocates inodes, though, so space not used for inodes couldn't hold other data anyway. (This is why ext4 has such a low number of free inodes (df -i), compared to XFS which can dynamically allocate as much space to inodes as it needs.) – Peter Cordes Sep 17 '15 at 2:48
1

Look ma, no loops!

Here is another solution, that may be slightly faster, because it doesn't use loops, but still arrives at the same answer.

unzip -l \*.zip|awk 'BEGIN{total=0}/        [0-9]+ files/{total=total+$1;}END{print "total bytes: "total}'

The "BEGIN{total=0}" part isn't strictly required.

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.