202
find . -type d

can be used to find all directories below some start point. But it returns the current directory (.) too, which may be undesired. How can it be excluded?

Deduplicator
  • 41,806
  • 6
  • 61
  • 104
Matthias Ronge
  • 7,765
  • 5
  • 38
  • 59

4 Answers4

219

Not only the recursion depth of find can be controlled by the -maxdepth parameter, the depth can also be limited from “top” using the corresponding -mindepth parameter. So what one actually needs is:

find . -mindepth 1 -type d
Matthias Ronge
  • 7,765
  • 5
  • 38
  • 59
210

POSIX 7 solution:

find . ! -path . -type d

For this particular case (.), golfs better than the mindepth solution (24 vs 26 chars), although this is probably slightly harder to type because of the !.

To exclude other directories, this will golf less well and requires a variable for DRYness:

D="long_name"
find "$D" ! -path "$D" -type d

My decision tree between ! and -mindepth:

  • script? Use ! for portability.
  • interactive session on GNU?
    • exclude .? Throw a coin.
    • exclude long_name? Use -mindepth.
  • if you need to exclude multiple paths just do `find /path/ ! -path "/path/first" ! -path "/path/second"` is this only way? – Vincent De Smet Jul 28 '15 at 11:28
  • 2
    @VincentDeSmet do you want to exclude just those paths, or actually not recurse into them? If just the paths, you can use `find / ! -regex '/\(a\|b\)/.*'` or more simply, pipe through grep. To not recurse, the above would be very inefficient and you should use `-prune`: http://stackoverflow.com/questions/1489277/how-to-use-prune-option-of-find-in-sh – Ciro Santilli新疆棉花TRUMP BAN BAD Jul 28 '15 at 12:12
  • my issue was as follows: I wanted to recursively delete everything within a directory except for 1 sub directory. I was using `find` with `grep` to exclude the directory but the parent directory was still there, causing everything to be deleted anyway. – Vincent De Smet Aug 09 '15 at 23:50
  • @VincentDeSmet I don't see a direct solution with `find`, you'd need to check for prefixes: http://stackoverflow.com/questions/17959317/bash-delete-all-files-and-directories-but-certain-ones But a Bash for loop can handle it :-) – Ciro Santilli新疆棉花TRUMP BAN BAD Aug 10 '15 at 08:43
  • You probably want to escape the exclamation char (`\!`) to be on the safe side. All the examples in my machine's `man find` have it escaped so it seems like it's probably a Good Idea™. Edit — Just noticed it even explicitly says: `! expr True if expr is false. This character will also usually need protection from interpretation by the shell.` – Adrian Günter Oct 28 '15 at 23:55
  • @AdrianGünter yes, this brings me memories: http://stackoverflow.com/questions/22125658/how-to-escape-history-expansion-exclamation-mark-inside-a-double-quoted-comm But for Bash (and POSIX?) I think it is not needed: `man bash` says: `! Start a history substitution, except when followed by a blank, newline, carriage return, =`. Otherwise we couldn't write things like `if ! true`. – Ciro Santilli新疆棉花TRUMP BAN BAD Oct 29 '15 at 05:52
  • @AdrianGünter I've looked at every POSIX occurrence of `!` but could not find anything that would expand in this case: http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html I think history expansion does not exist there. So maybe it is better to leave as is? Maybe a crazy non-conforming implementation would give it another meaning when it is escaped? (but yes, Bash is non-conformant already for doing history expansions?) – Ciro Santilli新疆棉花TRUMP BAN BAD Oct 29 '15 at 06:02
  • I realize this is an old answer, but for me, using this exact incantation still returns directories that begin with a dot, including the current dir. – rbellamy Oct 19 '16 at 18:25
  • @rbellamy the age of answers does not matter, and your comment is more than welcome. What we do need is your OS or `find` implementation name and version. I've just tested on Ubuntu 16.04, GNU findutils 4.7.0. – Ciro Santilli新疆棉花TRUMP BAN BAD Oct 19 '16 at 20:09
  • @CiroSantilli烏坎事件2016六四事件法轮功 - my bad - it does NOT include the current directory. It does return other dot-prefixed directories - but after reading the OP, I see that your answer only tries to exclude "current dir"? – rbellamy Oct 25 '16 at 03:09
22

I use find ./* <...> when I don't mind ignoring first-level dotfiles (the * glob doesn't match these by default in bash - see the 'dotglob' option in the shopt builtin: https://www.gnu.org/software/bash/manual/html_node/The-Shopt-Builtin.html).

eclipse tmp # find .
.
./screen
./screen/.testfile2
./.X11-unix
./.ICE-unix
./tmux-0
./tmux-0/default
eclipse tmp # find ./*
./screen
./screen/.testfile2
./tmux-0
./tmux-0/default
Milos Ivanovic
  • 510
  • 3
  • 8
  • 18
  • FYI. do not use this trick with `-exec`option. For example, if you try `find dir/* -type d -exec rmdir {} \;`, you will see errors. – plhn Mar 24 '17 at 03:05
  • You are mistaken, or perhaps misadvised. That command will work fine. If you are seeing errors, they will be coming from `rmdir` and are most likely telling you that the directories are not empty since `find` will do a depth-first search into the directories, showing the parents before their children. – Milos Ivanovic Mar 24 '17 at 23:10
  • 2
    Note: "ignoring first-level dotfiles" also means excluding all hidden files / directories. – Jonathan H Feb 21 '18 at 15:55
3

Well, a simple workaround as well (the solution was not working for me on windows git bash)

find * -type d

It might not be very performant, but gets the job done, and it's what we need sometimes.

[Edit] : As @AlexanderMills commented it will not show up hidden directories in the root location (eg ./.hidden), but it will show hidden subdirectories (eg. ./folder/.hiddenSub). [Tested with git bash on windows]

StackHola
  • 848
  • 7
  • 16
  • Use shopt -s dotglob before this, and you'll match every dotfile, except the one for the current folder. Tested on both bash version 3.2 and 5.0. – MetalGodwin Nov 06 '20 at 15:40