I need a script or command that prints a number of directories which name begins from "lib" in whole directory subtree. I was trying to do it using find, grep, and wc but can't scan all directories. How to do it?

  • I replaced grub with grep; hope that's what you meant. Revert if not. – Jeff Schaller Jun 29 '17 at 12:15
find . -type d -name lib\* -exec echo x \; | wc -l
  • That does run one echo command in a new process for each lib* directory though, which is not efficient. – Stéphane Chazelas Jun 29 '17 at 13:33
  • I doubt there's any reason to use echo. find . -type d -name lib\* -printf "%i\n" | wc -l should be sufficient. Let find handle everything instead of calling extra process there. – Sergiy Kolodyazhnyy Jun 30 '17 at 21:18
  • @SergiyKolodyazhnyy -printf is gnu specific – Stéphane Chazelas Jun 30 '17 at 22:06
  • @StéphaneChazelas didn't know. Will remember – Sergiy Kolodyazhnyy Jun 30 '17 at 22:10
LC_ALL=C find .//. -name 'lib*' -type d | grep -c //

You can't use find . | wc -l as that wouldn't work properly if there are file paths with newline characters.

Without LC_ALL=C that could fail to count dir names that start with lib but where the rest of the name contains bytes that don't form valid characters.

With zsh:

(){echo $#} **/lib*(DN/)
  • 1
    Neat trick with .//.... – Kusalananda Jun 29 '17 at 13:33
  • Wait, can you explain how does .//. work ? I get the idea that grep -c should be able to count lines starting with that pattern, but it seems like after ./ should only be filenames and yet there's another slash dot there, and it still works. How ? – Sergiy Kolodyazhnyy Jun 30 '17 at 21:50
  • @SergiyKolodyazhnyy . is the current directory, which may also be spelled ./. Slashes can be repeated, so .// is still the current directory, and .//. is the same as well (foo/. is always the same as foo). find will print its results relative to the root directory you specified, in this case like .//./libwhatever, .//./foo/bar/libsomething, etc. – Tavian Barnes Jul 3 '17 at 3:00

Assuming all lib-directories have sane names with no newlines or other strange/exotic characters:

find / -type d -name "lib*" -print | wc -l

This assumes that you, by the "whole directory subtree", mean "anywhere". Change / into . to only count in the current directory or below.

The find command would find all directories (-type d) whose name start with lib (-name "lib*") and print these (-print).

The wc -l would count the number of lines in the output from find.

  • I'm not sure about BSD find, but at least with GNU find -print is default behavior, so we could leave that off, no ? And as I commented on other answer, the strange/exotic filenames probably can be handled with -printf "%i\n" option instead – Sergiy Kolodyazhnyy Jun 30 '17 at 21:53

Here's a bash-only solution, using globstar shell option:

$ shopt -s globstar; c=0; for f in ** ; do echo "${f##*/}"; [[ "${f##*/}" =~ ^lib* ]] && [[ -d "$f" ]]  && ((c++)); done ; echo $c

Or in script format for readability:

#!/usr/bin/env bash
shopt -s globstar 
for f in **
    echo "${f##*/}"
    if [[ "${f##*/}" =~ ^lib* ]] && [[ -d "$f" ]]
echo $c

globstar allows us to enable recursive globbing and via [[ with pattern matching and -d flag to check if that's a directory, we increment the counter variable $c, which we print at the end.

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.