Take the 2-minute tour ×
Super User is a question and answer site for computer enthusiasts and power users. It's 100% free, no registration required.

How can I find all subdirectories in a directory (only 1 level down) that do not contain a given file ?

share|improve this question
    
How have you tried doing this? Do you want for perform this with Bash? I.e. through the command line. –  wonea Feb 8 '13 at 16:19

2 Answers 2

There are several ways to accomplish this with find.

Approach 1 is less hacky and will work well with strange directory names (e.g., directory names containing newlines), but approach 2 should be faster if there are a lot of directories.

Approach 1

Command

find DIR -type d -mindepth 1 -maxdepth 1 -not -exec test -f {}/FILENAME \; \
    -print | sort

How it works

  • find DIR -type d -mindepth 1 -maxdepth 1 finds all directories (-type d) in DIR, with depth 1.

  • -not -exec test -f {}/FILENAME \; is true if and only if a file called FILENAME could not be found in the currently processed directory ({}).

  • -print will output the desired directory names.

  • If desired, sort will sort the output alphabetically.

Approach 2

Command

( find DIR -type f -mindepth 2 -maxdepth 2 -name FILENAME -printf "%h\n" ; \
    find DIR -type d -mindepth 1 -maxdepth 1 ) | sort | uniq -u

How it works

  • find DIR -type f -mindepth 2 -maxdepth 2 -name FILENAME finds all files (-type f) called FILENAME in the subdirectories of DIR (files of directories of depth 1 have depth 2).

  • -print "%h\n" print the names of the directories containing files named FILENAME, followed by a newline.

  • find DIR -type d -mindepth 1 -maxdepth 1 list all directories (-type d) in DIR, with depth 1.

  • sort sorts the output alphabetically (output must be sorted when piping to uniq).

  • uniq -u prints only unique lines.

    Every subdirectory of DIR gets listed at least once, but those that contain a file called FILENAME get listed twice. uniq -u eliminates the latter kind.

share|improve this answer

Directly in shell script:

for i in DIRECTORY/*/; do [ -f "$i/FILENAME" ] || basename "$i"; done
  • for i in DIRECTORY/*/ uses shell expansion to safely (shouldn't be any problems with strange directory names) give all sub directories of a specific directory. Note the trailing slash to only give the directories.
  • [ -f "$i/FILENAME" ] returns true if the file named FILENAME exists in the directory in this iteration. The || operator makes the following command run if the first one returned false, i.e. only if the file did not exist in the directory.
  • basename "$i" prints the directory name (if the file name wasn't found therein). If you want the full path and not just the directory name, substitute basename for echo or readlink -f or something else per preference.

If you also want to include hidden directories, run (in Bash)

shopt -s dotglob

before the command.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.