List zip files which have less than a specific number of files

Solution 1:

for z in *.zip; do if (( $(unzip -l "$z" | sed -nr '$ s/.* ([0-9]+) files?/\1/p') < 15 )); then echo "$z"; fi; done

This lists the .zip files with less than 15 files to stdout (in the terminal), so if you want to create a list file, you can tee out or redirect. Here it is more readably, creating a list file at the end as well as printing in the terminal

for z in *.zip; do 
   if (( $(unzip -l "$z" | sed -nr '$ s/.* ([0-9]+) files?/\1/p') < 15 )); then 
      echo "$z"
   fi
done | tee small-zip-list

Notes

  • for z in *.zip loop over files ending with .zip and do something to each one, represented by the variable z referenced with $z
  • if (( $(unzip -l "$z" | sed -nr '$ s/.* ([0-9]+) files?/\1/p') < 15 )) make unzip count the files, extract the number from the output (there's surely a tidier way to extract only the number, but I know sed so I've used it - see @muru's comment for a simpler way that may be faster with many files) and test whether it's less than 15, and if it is
  • echo "$z" then print the filename
  • | tee small-zip-list also print output to a new file, as well as in the terminal

Solution 2:

A late python option, using python's zipfile, (as suggested by @muru, thanks!)

#!/usr/bin/env python3
import os
import sys
from zipfile import ZipFile

dr = sys.argv[1]

for zp in [os.path.join(dr, f) for f in os.listdir(dr) if f.endswith(".zip")]:
    if len(ZipFile(zp, "r").namelist()) < int(sys.argv[2]):
        print(zp)

How to use

  1. Copy the script into an empty file, save it as get_zips.py
  2. Run it with the directory and the desired (minimum) number of files inside, e.g.:

    python3 /path/to/get_zips.py /full/path/to/directory_with_zips 15
    

Explanation

The script:

  • lists .zip files inside a directory:

    for zp in [os.path.join(dr, f) for f in os.listdir(dr) if f.endswith(".zip")]:
    
  • Looks inside the file and counts the number of files:

    if len(ZipFile(file, "r").namelist()) < n:
        print(file)
    

    Only prints the file(+path) if the number of listed items is smaller then n.

Solution 3:

Using awk:

for i in ~/path/to/your/folder/*.zip; do if (( $(unzip -l $i | awk 'END {print $(NF-1)}') < 15 )); then echo "$i"; fi; done

Or it can be also done with script.

Create script zip.sh

#!/bin/bash

for i in ~/path/to/your/folder/*.zip; do
    if (( $(unzip -l $i | awk 'END {print $(NF-1)}') < 15 )); then
        echo "$i"
    fi
done

Save it in home folder & Make it executable with chmod +x zip.sh and run from terminal ./zip.sh

Here, if (( $(unzip -l $i | awk 'END {print $(NF-1)}') < 15 )),

  • unzip -l $i it will count the number of files from respective zip file & from its output,

  • awk 'END {print $(NF-1)}' grep that count number only, if it is less than 15 then it will print the filename.