Quantcast
Channel: User Kamil Maciorowski - Super User
Viewing all articles
Browse latest Browse all 648

Answer by Kamil Maciorowski for How do I list all directories with include same name subdirectory?

$
0
0

With GNU find:

find /music -type d -execdir test -d {}/{} \; -print

For each file of the type directory test will be run. The trick is in how -execdir differs from -exec: for a directory named something, test will be run in its parent directory and {} will be expanded to ./something. In effect the actual inside command will be test -d ./something/./something, equivalent to test -d something/something. Then we use the fact that -execdir … \; (like -exec … \;) is not only an action, it is a test that succeeds iff the exit status from whatever it runs is 0. test -d checks if the given pathname is the name of a directory, it reports success via its exit status.

This way -print will be evaluated only (almost only, see just below) for directories containing directories of the same name.

Notes:

  1. test -d succeeds also for a symlink to a directory. You can add ! -execdir test -L {}/{} \; (before -print) to ignore cases where something/something is a symlink.

  2. In general remember that {} expands to what find considers the pathname of the currently processed file, not necessarily to what a command like realpath … would print. Similarly -name tests what find considers the name, not necessarily to what basename "$(realpath …)" would print. E.g. if the starting point is /music then at some point our command will test ./music/./music inside /music/.., so effectively /music/music which may or may not be an existing directory and the test is conclusive, which is fine. But:

    • If the (or a) starting point was . (or e.g. /music/.!), then it would be reported because test -d would test ./././. which is a directory for sure (regardless of the working directory of test), so the test is not conclusive. ! -name . is a test that "fixes" this, i.e. it unconditionally excludes .. But now we can miss a directory: if the current working directory is /music and /music/music exists, we won't detect it.

      In general passing . as a starting point to find is quite common. In our case, if you need to start at ., consider find "$PWD" … instead.

    • Similarly if the (or a) starting point was .. (or e.g. /music/artist/..), then it would be reported because test -d would test ./.././.. which is a directory for sure (regardless of the working directory of test). ! -name .. is a test that "fixes" this, but again we can miss one specific directory. In general passing .. as a starting point to find is uncommon, still possible.

    If the starting point is /music then there will be no problem.

  3. The successful detection occurs when find processes the upper (closer to /) directory from each something/something pair, not the lower one. This has the following consequences:

    • Our command will print paths like /music/foo/bar/something (as opposed to /music/foo/bar/something/something)

    • For -mindepth or -maxdepth (if you decide to use them), the depth of the upper directory matters.

    • If you want to automate doing something to one of the directories of the pair:

      • work with the upper one by passing {} after -exec or -execdir;
      • but the easiest way to address the lower one is to use {}/{} after -execdir like we did; in general this will not work after -exec.

Viewing all articles
Browse latest Browse all 648

Trending Articles