3

I want to find all subdirectories inside a single directory. I need to filter by the name length. I have come up with the following:

find ./directory -maxdepth 1 -type d

How do I filter the results by the name length -- for example, only subdirectories with more than 4 characters?

2 Answers 2

8

You could use the ? wildcard five times:

find ./directory -maxdepth 1 -type d -name '?????*'

This requires matches to have at least five characters in their name.

3
  • 2
    +1 And for future readers who may want to filter the results of find based on a maximum filename length, find ./directory -maxdepth 1 -type d -not -name '?????*' will find directories with 4 or fewer characters in the name.
    – Jim L.
    May 17 at 21:10
  • 2
    @JimL, with GNU find at least, that would also report directory names that contain bytes that can't be decoded into characters regardless of their length. May 17 at 21:15
  • 1
    Thank you for that detail, @StéphaneChazelas. I would encourage you to post a general solution that solves the question as titled, rather than the question as stated. I.e., to be able to filter find's results based on a minimum, maximum, or indeed, a range of lengths.
    – Jim L.
    May 17 at 21:20
7

With the zsh shell, globs can be filtered by file type (here directory) using glob qualifiers, and with the extendedglob option on, you get a (#cX,Y) glob operator similar to ERE {X,Y}, so you could do:

set -o extendedglob
print -rC1 -- ?(#c5,)(ND/)

To print raw on 1 Column the files of type directory (/ glob qualifier) that are made of 5 or more characters (?).

?(#c5,12) would be 5 to 12 characters, ?(#c,5) or ?(#c0,5) 0 to 5 (not that you can get a filename with 0 characters).

Remove the D qualifier if you want to ignore hidden ones.

For zsh globs, any byte that cannot be decoded into a character is still considered as one (and will be matched by ?).

For some find implementations including GNU find, at least on GNu systems (using the GNU regex / fnmatch() from the GNU libc) they will match neither ? nor * (nor regex .) so:

find . -maxdepth -name '?????*' -type d

or

find . -maxdepth 1 -regextype posix-extended -regex '.*/[^/]{5,}' -type d

would fail to match a $'St\xe9phane' directory for instance if run in a UTF-8 locale as that 0xe9 byte (ISO8859-1 é) cannot be decoded into a character, and there's no workaround other than running find under LC_ALL=C but then you count the length in bytes instead of characters.

The BSD equivalent of that -regex one, would be:

find -E . -maxdepth 1 -regex '.*/[^/]{5,}'

or

find . -maxdepth 1 -regex '.*/[^/]\{5,\}'

(the default being basic regular expressions while for GNU find, it's some old emacs regex dialect).

bearing in mind that multibyte character support will vary between variants.

If on a GNU system, another approach is to get find to output the file paths NUL delimited (on the ground that NUL is the only character that can't be found in a file path) and gawk to filter by length of their name:

find . -mindepth 1 -maxdepth 1 -printf '%P\0' |
  gawk -v RS='\0' -F/ 'length($NF) > 4'

If there are bytes that can't be decoded into characters, gawk issues a warning but each of those bytes contribute 1 to the length as with zsh.

7
  • Thank you for providing an example of the bug in GNU find. FWIW, BSD find appears to handle that particular case correctly (CHARSET=UTF-8), matching on -name ????????* but not -name ?????????* and likewise, matching on -not -name '?????????*' but not on -not -name '????????*'.
    – Jim L.
    May 17 at 21:32
  • @JimL, I suspect your BSD find doesn't support multi-byte characters and would find a UTF-8 encoded ééé as it has 6 bytes and 3 characters. You may want to clarify which BSD you're using. There's usually significant differences between each. May 17 at 21:35
  • I'm happy to test, if you can create an example of how to create such.
    – Jim L.
    May 17 at 21:36
  • @JimL, check with locale charmap that you're in a UTF-8 locale, touch ééé, and check which of find ééé -name '???' or find ééé -name '??????' returns it. May 17 at 21:40
  • As above, charmap is UTF-8. find ééé -name '???' matches it, find ééé -name '??????' does not. But is it a multi-byte character? Hex E9 is still a single byte.
    – Jim L.
    May 17 at 21:43

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.