0

I have several directories like so :

Rep1/foo/bar/files

Rep2/foo/otter/files

Rep3/foo/bar/files

...

What i'd like to have is the name of the directories that doesn't have the subdirectory */bar/ and possibly remove them. How can I do that ?

4
  • Do you really want all directories without a bar subdirectory or do you want only the top level directories, in this case Rep2/? Strictly speaking Rep2/foo and Rep1/foo/otter also contain no bar. Same goes for C/ in /A/bar/C/.
    – Socowi
    May 10, 2019 at 9:36
  • I just want the name of the top level directories in order to remove them afterwards May 10, 2019 at 9:44
  • How about the depth of the directory tree... will it be a static 3 subdirectories ?
    – Siva
    May 10, 2019 at 9:46
  • 1
    Please edit your question and give us some more details. First, what OS is this on? Can we assume GNU tools or not? Second, as others have said, please clarify your desired output, whether the directory structure is always the same, whether you can have things like Rep2/foo/bar/files as well as Rep2/foo/otter/files. And, if yes, should Rep2/foo/otter/ be returned?
    – terdon
    May 10, 2019 at 9:50

4 Answers 4

2

You can use the following script using find

for topdir in ./*/; do
    [ -z "$(find "$topdir" -type d -name bar -print -quit)" ] &&
    echo "$topdir"
done

The -print -quit part is just an optimization to exit once a bar/ sub-directory is found. If your version of find doesn't support -quit you can just remove that part. The command will still work but could be a tiny bit slower.

or this pure bash script

shopt -s globstar
for topdir in ./*/; do
    (cd "$topdir" && compgen -G '**/bar/' > /dev/null) ||
    echo "$topdir"
done

If you are happy with the results replace echo "$topdir" with rm -r "$topdir" to delete the directories.

Both approaches can handle arbitrary file/directory names, even such with linebreaks or special symbols like * in them.

4
  • Note that -quit is a GNU extension. May 10, 2019 at 11:37
  • Thanks for the hint. Luckily it's only an optimization. Non-GNU find users can still use the same approach. I edited the answer to point out how to do it.
    – Socowi
    May 10, 2019 at 11:57
  • Does the -print do anything useful here? Won't find "$topdir" -type d -name bar -print -quit d exactly the same thing as find "$topdir" -type d -name bar -quit?
    – terdon
    May 10, 2019 at 11:59
  • The -print is needed because of the -quit. If -quit wasn't there then -print could be left out. You can test it yourself: find / -print -quit prints / but find / -quit prints nothing.
    – Socowi
    May 10, 2019 at 12:00
0

Use bash command line to find all **/files subdirectories whithout /bar/ in the path:

find . -type d -name files | grep --invert-match /bar/

It gets you an output:

./Rep2/foo/otter/files

Now if you want to list only top level directories use:

find . -type d -name files | grep --invert-match /bar/ | cut --delimiter / --fields 2

It gives you:

Rep2
1
  • With the assumption that there is always exactly one files/ directory you could as well use find -type d -name files -not -path '*/bar/*' | cut -d/ -f2 instead of the the find+grep combination.
    – Socowi
    May 10, 2019 at 10:10
0

Try this,

find . -mindepth 3 -maxdepth 3 ! -iname bar -type d | awk -F'/' '{print $2}'

Rep2
0

With zsh:

has_bar() () (($#)) ${1-$REPLY}/*/bar(N/)

echo rm -rf rep*(/^+has_bar)

Or in a single command:

echo rm -rf rep*(/^e'[() (($#)) $REPLY/*/bar(N/)]')

(remove echo when happy)

You must log in to answer this question.

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