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 ?

  • 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 at 9:36
  • I just want the name of the top level directories in order to remove them afterwards – A_Concerned_Sysadmin May 10 at 9:44
  • How about the depth of the directory tree... will it be a static 3 subdirectories ? – msp9011 May 10 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 at 9:50
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.

  • Note that -quit is a GNU extension. – Stéphane Chazelas May 10 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 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 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 at 12:00
  • Ah, of course. Thanks. – terdon May 10 at 12:01
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
  • 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 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)

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.