Fd: Using fd to count files per directory...but elegantly

Created on 9 Oct 2020  ·  4Comments  ·  Source: sharkdp/fd

What version of fd are you using?
[paste the output of fd --version here]
fd 8.1.1

Another in my series of "Let's replace find" tasks (while learning bash as well). This should be simple, but I'm missing it. To wit, on a cluster I'm on we have inode limits, thus when I get close I often run a script:

#!/bin/bash
for item in *
do
   if [[ -d $item ]]
   then
      num=$(find $item | wc -l)
      echo $num $item
   fi
done

that counts up the files in a set of subdirs. So if I run that in a directory I get, say:

$ time files_per_directory
48779 CURRENT
48779 PREVIOUS

real    0m20.946s
user    0m0.300s
sys 0m0.788s

Now I can do this:

#!/bin/bash
for item in *
do
   if [[ -d $item ]]
   then
      cd $item
      num=$(fd --no-ignore-vcs | wc -l)
      cd ..
      echo $num $item
   fi
done

which works and is fast:

$ time files_per_directory_fd
47666 CURRENT
47666 PREVIOUS

real    0m1.776s
user    0m1.048s
sys 0m1.024s

It just seems...clunky to me (not sure what files I might be missing, but close enough).

Is there a better/more elegant way of doing it that doesn't involve changing directories?

question

All 4 comments

You can use fd . "$item" --no-ignore --hidden to avoid having to change directories, since the pattern "." will always match.

Another option is the --base-directory <path> argument.

If you want the whole script in a single command (who wouldn't? :smile:), you could run something like:

fd -d1 -td -x bash -c "echo -n '{} '; fd -HI --base-directory '{}' | wc -l"

Note that this is vulnerable to shell injection (if a directory name contains a single quote ') - which is why I would not recommend using this. Also, I don't really see a problem with your script. That's what bash scripts are made for. If you want to simplify it, you can replace the

for item in *
do
   if [[ -d $item ]]
   then
     ……
    fi
done

part by

for dir in */
do
   ……
done

Also, If you really want to count files per directory, you would have to use --type file.

I'm not a huge fan of globs, and I realize this isn't fd (I'm using depth-pinning with find, not sure if there's something comparable in fd), but maybe it gives some ideas?

❯ find . | xargs file
.:          directory
./d1:       directory
./d1/f3:    empty
./d1/f2:    empty
./d1/f1:    empty
./d3:       directory
./d3/d4:    directory
./d3/d4/f4: empty
./d3/d4/f5: empty
./d2:       directory
./d2/f6:    empty

❯ for d in $(find . -type d -mindepth 1); do echo "$d $(find $d -maxdepth 1 -type f | wc -l)"; done
./d1        3
./d3        0
./d3/d4        2
./d2        1

Closing this, as the question has been answered, IMO

Was this page helpful?
0 / 5 - 0 ratings