7

I need to do some folder clean-up, and I'd like to remove directories that have 10 or fewer files in them. I tried looking at GNU's find, but there wasn't anything related to the number of files in a directory. What commands should I use to accomplish that?

  • You need to mention if this is a recursive directory or a flat one. It makes quite a difference. Also if you mean items (also subdirs) or strictly files. – Jacob Vlijm Jul 21 '16 at 22:00
  • Could you please include an example in your question to illustrate the problem? I'm not sure I understand all the details. – David Foerster Aug 4 '16 at 9:40
  • CLOSE_VOTERS What you didn't understand from this clear question? – Anwar Aug 8 '16 at 4:14
3

One way would be to use find's -exec action to execute a custom test of the number of files.

One could use a second find command along with wc to find and count files within each directory, but probably a better option would be to shell globbing to slurp the filenames into an array, and then return a logical value indicating whether the size of the array is less than the threshold i.e.

files=( dir/* ); ((${#files[@]} < 10))

Putting it all together, we should be able to list all subdirectories with fewer than 10 files (including sub-subdirectories) using

find . -depth -type d ! -name '.' -exec bash -c '
  shopt -s nullglob; files=( "$1"/* ); ((${#files[@]} < ${2:-10}))
  ' bash {} 10 \; -print0 | xargs -0

(you can adjust the number 10 after the bash {} for different thresholds - the ${2:-10} parameter expansion makes it default to 10 files if no second argument is given). For example, given

$ tree .
.
├── bar
│   ├── file1
│   ├── file2
│   ├── file3
│   ├── file4
│   ├── file5
│   ├── file6
│   ├── file7
│   ├── file8
│   ├── file9
│   └── other file
├── baz
│   ├── other file
│   └── subdir
└── foo
    └── somefile

4 directories, 12 files

then

find . -depth -type d ! -name '.' -exec bash -c '
  shopt -s nullglob; files=( "$1"/* ); ((${#files[@]} < "${2:-10}"))
  ' bash {} 10 \; -print0 | xargs -0
./foo ./baz/subdir ./baz

If that appears to be doing the right thing you can actually remove them by adding rm -rf but please be very careful with this - remember there is no 'undo'

find . -depth -type d ! -name '.' -exec bash -c '
  shopt -s nullglob; files=( "$1"/* ); ((${#files[@]} < ${2:-10}))
' bash {} 10 \; -print0 | xargs -0 rm -rf

(The xargs could be eliminated by using another -exec action to run rm more directly, but the formulation above makes it easy to generalize - print, stat, remove or whatever.)

Note this will act recursively i.e. a directory that initially has more than n files including subdirectories may be removed as a result of some of those subdirectories themselves getting removed as find backs up the directory tree - the current directory is explicitly protected from possible deletion by the ! -name '.' test.

If you don't need it to act recursively, you can simply loop over directories and perform the same file count and test logic e.g. to remove all first-level directories containing fewer than 10 files in the current directory

shopt -s nullglob
for d in */; do
  files=( "$d"/* )
  ((${#files[@]} < 10)) && echo rm -rf -- "$d"
done 
0

I didn't fully test this, but it should work. You can also use find to accomplish this.

# Loop over all files
for dir in * **/*; do
    # Make sure this is a directory
    if [ -d "${dir}" ]; then
        # Get the number of files (not including directories) in this directory
        FILE_COUNT=`ls $dir -1 | grep -v / | wc -l`
        # Check if it's less than 10
        if [ $FILE_COUNT -lt 10 ]; then
            # Delete the directory
            rm -rf "${dir}"
        fi
    fi
done
  • Note that using wc -l will give the wrong number if there are newlines in any filenames. – wjandrea Jul 21 '16 at 21:58

Your Answer

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