Bash – Listing number of files in each folder where folder name matches a pattern

bashfindlswcxargs

I would like to recursively search a directory tree and list the number of files available in each folder that have a name corresponding to a given string. In effect, I would like to return results of:

ls -l | wc -l

for each folder I've identified via find.

Code

The code below correctly lists number of files in each folder that name contains Magic Data string.

find /path/to/cool/stuff                        \         
        -maxdepth 4                             \         
        -type d                                 \         
        -name '*Magic Data*'                    \         
        -print0 | xargs                         \         
                --verbose                       \         
                -0 -I {} ls -l {}               

I would like to expand it and pipe results of ls to wc -l.

Attempt

find /path/to/cool/stuff                        \         
        -maxdepth 4                             \         
        -type d                                 \         
        -name '*Magic Data*'                    \         
        -print0 | xargs                         \         
                --verbose                       \         
                -0 -I {} ls -l {}  | wc -l            

This fails and prints:

ls -l /path/to/cool/stuff/some/folders/Magic Data
ls -l /path/to/cool/stuff/some/folders2/Magic Data

Desired output

/path/to/cool/stuff/some/folders/Magic Data  29
/path/to/cool/stuff/some/folders2/Magic Data 30

Desired output would contain:

  • Full path to the searched folder
  • Output of wc -l, which in this case reflects count of non-hidden files

Best Answer

There are two main ways of solving this:

  1. Modify the find command so that it only enters the directories that you are interested in and then prints a single character (e.g. x) for each found file within. Then count the number of that character produced with wc -l. It's safer to output a character than the pathname as pathnames in Unix could potentially contain newlines. This solution is a bit tricky as it involves using -prune to ignore directories we're not interested in, or alternatively ! -path.

  2. Find the directories just like you've done so far, but then use an in-line script to do the counting of files within. This is simpler, and what I'm showing below.

find /path/to/cool/stuff -maxdepth 4 -type d -name "*Magic Data*' \
    -exec bash -O dotglob -c '
        dir=$1
        set -- "$dir"/*
        printf "%s %d\n" "$dir" "$#"' bash {} ';'

Here, we find the directories like you do, then, for each directory, we run this short bash script:

dir=$1
set -- "$dir"/*
printf "%s %d\n" "$dir" "$#"

This takes the pathname of the directory from the command line (given by find), and expands the * glob pattern within it. By setting the dotglob shell option on the command line for the script, we are guaranteed to also count hidden files and directories (remove -O dotglob to not count hidden names).

We do the expansion of all names as an argument to set, which will set the positional parameters to the expanded entries. The number of positional parameters is available as $#, which is therefore also the count of files in that particular directory.

Related:

Related Question