4

I want to find all directories that are named Contents and then from the list of found results, I want to filter them and only include those that have a file named Database.json directly inside them.

find / -type d -name Contents 2>/dev/null |
while read dir;
do
    if [ -f $dir/Database.json ]; then
        echo $dir
    fi
done

This code works. But I think it's overkill. I think there should be an easier way to do it.

Can I rewrite this code to become simpler?

1
  • 1
    have you try something like: ls **/Contents/Database.json? Commented Jul 5, 2022 at 5:34

1 Answer 1

6

With GNU find, you can do:

LC_ALL=C find . -path '*/Contents/Database.json' -printf '%h\n'

Where %h gives you the head of the path, that is the dirname.

LC_ALL=C is needed for * to match any sequence of bytes regardless of what the user's locale regards as characters, as file paths can be made of any sequence of bytes other than 0 which don't have to make up characters in the user's locale.

Similar with zsh, also giving you a sorted list, excluding hidden ones, add also reporting symlinks to directories that contain a Database.json:

print -rC1 -- **/Contents/Database.json(N:h)

For a closer equivalent to GNU find's approach:

print -rC1 -- **/Contents(NDoN/e['
  [[ -e $REPLY/Database.json || -L $REPLY/Database.json ]]'])

Where:

  • D includes hidden files (Dot files)
  • oN does Not order the list.
  • / selects the files of type directory.
  • e['code'] checks that the dirs contain a Database.json file of any type including broken symLink.

In any case, you cannot post-process the list like you do there. Instead, you'd do:

With GNU find and zsh or bash:

while IFS= read -ru3 -d '' file; do
  something with "$file"
done 3< <(LC_ALL=C find . -path '*/Contents/Database.json' -printf '%h\0')

With zsh:

for file (**/Contents/Database.json(N:h)) something with $file

See also:

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .