10

How would I go into listing all directories that do not have a file with a given file name inside? e.g. given this tree

/
  /a
     README
     file001
     file002
  /b
     README
     file001
  /c
     file003

I want to list the directories that do not have a file named README, in this case it would be directory /c. How would I do that? I can't think of any syntax using e.g. find.

5

Assuming a find implementation like GNU find that accepts a {} embedded in an argument to -exec:

$ find . -type d \! -exec test -e '{}/README' \; -print

Example

Here directories 1/1 through 5/5 have a README, the other dirs are empty.

$ tree 
.
|-- 1
|   `-- 1
|       `-- README
|-- 10
|   `-- 10
|-- 2
|   `-- 2
|       `-- README
|-- 3
|   `-- 3
|       `-- README
|-- 4
|   `-- 4
|       `-- README
|-- 5
|   `-- 5
|       `-- README
|-- 6
|   `-- 6
|-- 7
|   `-- 7
|-- 8
|   `-- 8
`-- 9
    `-- 9

Now when we run this version of our find command:

$ find . -type d \! -exec test -e '{}/README' \; -print
.
./10
./10/10
./7
./7/7
./9
./9/9
./6
./6/6
./5
./8
./8/8
./4
./1
./3
./2

References

  • How to modify the command to search for sub directories that dont have a specific file extension (say *.txt). Modifying README with *.txt doesnt seem to work – WanderingMind Apr 16 '16 at 17:20
  • @WanderingMind - if you have a new question please ask it as a new one on the site ;-) – slm Apr 16 '16 at 20:53
3

You can use the -exec option of find to check for the file, and then print all results for which the check fails.

find /path/to/base -mindepth 1 -maxdepth 1 -type d -exec test -e {}/README \; -o -print
3

No need for find. Just use the shell:

for d in */; do [ -f "$d"README ] || printf '%s\n' "$d"; done
c/

If you need it to be recursive, you can use (for bash, zsh can do this by default, use set -o globstar in ksh93):

shopt -s globstar
for d in **/; do [ -f "$d"README ] || printf '%s\n' "$d"; done

(note that dot-files are excluded by default).

2

With zsh and glob qualifiers (estring):

print -rl -- *(/e_'[[ ! -f $REPLY/README ]]'_)

or

print -rl -- *(/^e_'[[ -f $REPLY/README ]]'_)

add D to include hidden directories:

print -rl -- *(D/e_'[[ ! -f $REPLY/README ]]'_)

/ selects only directories and e_'[[ ! -f $REPLY/README ]]'_ further selects only the directory names for which the shell code between the quotes returns true, that is for each directory name ($REPLY) that the glob *(/) expands to, it runs [[ ! -f $REPLY/README ]] and keeps the name of the directory if the result is true.
The second form ^e_'.....'_ uses the same glob qualifier, negated (but this time the conditional expression isn't negated: [[ -f $REPLY/README ]]).


The above will only return directory names in the current directory.
If you want to search recursively (again, to include hidden directories add the D qualifier):

print -rl ./**/*(/e_'[[ ! -f $REPLY/README ]]'_)
2

Portably, you could do:

find . -type d -exec sh -c '
  for dir do
    [ -f "$dir/README" ] || printf "%s\n" "$dir"
  done' sh '{}' +

[ -f file ] tests if the file exists and is confirmed to be a regular file (after symlink resolution).

If you wanted to test that it exists only (as an entry in that directory), regardless of its type, you'd need: [ -e file ] || [ -L file ], though note that you need search permission to the directory to perform those tests. You may want to add some [ -x "$dir" ] tests to account for those cases like:

find . -type d -exec sh -c '
  for dir do
    if [ -x "$dir" ]; then
      [ -f "$dir/README" ] || printf "%s\n" "$dir"
    else
      printf >&2 "Cannot tell for \"%s\"\n" "$dir"
    fi
  done' sh '{}' +

Or to avoid the race condition, with zsh:

find . -type d -exec zsh -c '
  zmodload zsh/system
  for dir do
    ERRNO=0
    if [ ! -f "$dir/README" ]; then
      if [ "$errnos[ERRNO]" = ENOENT ]; then
        printf "%s\n" "$dir"
      else
        syserror -p "ERROR: $dir/README: "
      fi
    fi
  done' zsh '{}' +

See also How do I tell if a regular file does not exist in Bash? on SO.

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.