APLawrence.com -  Resources for Unix and Linux Systems, Bloggers and the self-employed

Removing empty directories


2013/08/12

While cleaning out this site I came across something I had saved from over ten years ago. It was an interesting little thread that introduced me to a command I didn't know at the time: "tac". This post isn't really about "tac", but in case you've never heard of it, we'll take a minute to explain that it simply reverses whatever you give it for input. That makes it handy for looking at log files: the latest entries will be at the top if you do "tac logfile | less".

Let's digress

My OS X system does not have "tac". It does have "rev", which is not at all the same (it reverses the characters of each line, so "123" becomes "321"). This is a strange omission because OS X embraces most other GNU utilities, but there it is: no "tac" in OS X.

We needn't fret, though. Sed can do this easily:


sed '1!G;h;$!d'
 

will do exactly what "tac" does, albeit without any of the flags described in the man/info pages. If you are wondering how that works, see "tac" in sed, which explains:

sed '1!G;h;$!d' does this to each line in turn:-
If it's not line1, append '\nHold space contents' to it (\n = newline);
Then copy it to the hold space (deletes previous contents);
Then, if it's not the last line, delete it.

The whole file ends up on the last line, other lines having been deleted.
 

Easier to remember is "tail -r logfile" which is also a bit less typing than "tac logfile" | head"

Back to 2001

The old newsgroup post had nothing to do with log files. Instead, this slightly unusual request had been made:

Is there a way to quickly find and  delete all empty directories within a certain directory?
 

You might imagine something like this:

$ find . -ls
35965024        0 drwxr-xr-x    4 tony             staff                 136 Aug 12 06:43 .
62784222        0 drwxr-xr-x    3 tony             staff                 102 Aug 12 06:43 ./b
62784223        0 drwxr-xr-x    3 tony             staff                 102 Aug 12 06:43 ./b/c
62784224        0 drwxr-xr-x    3 tony             staff                 102 Aug 12 06:43 ./b/c/d
62784225        0 drwxr-xr-x    2 tony             staff                  68 Aug 12 06:43 ./b/c/d/f
62784226        0 drwxr-xr-x    3 tony             staff                 102 Aug 12 06:43 ./c
62784227        0 drwxr-xr-x    3 tony             staff                 102 Aug 12 06:43 ./c/d
62784228        0 drwxr-xr-x    3 tony             staff                 102 Aug 12 06:44 ./c/d/f
62784234        0 -rw-r--r--    1 tony             staff                   0 Aug 12 06:44 ./c/d/f/file
 

The challenge would be to remove ./b and everythiing under it (because there is nothing but more empty directories under b) but to leave ./c alone (because ./c/d/f is not empty).

Hold your horses! Yes, there is a very simple, very quick solution. I'm not sure that the "find" the posters were using back then had everything they would have needed, but I'm pretty sure it had most of it. To spare us the silliness of someone jumping to the comments to breathlessly give the solution, I'll spoil it myself and tell you that

find . -type d -delete 
 

does what was requested.

But let's segue

So why didn't the folks responding to that thread use that and how did "tac" get mixed up in it?

Let's let the person who suggested "tac" explain:

find $1 -type d | 
while read D 
do 
	l $D | grep -q 'total 0' && rmdir $D 
done

However, if you have ./a/b/c, where c is a directory, and b has
nothing but c in it, this will still leave you with /a/b until you
run the script again.
 

Once again I have to ask the overly anxious folks to keep their fingers off the keyboard. Yes, both I and other posters at that thread know about "-depth" in "find", so you don't need to jump to the comments to explain that. I'll do that right here.

What that poster was talking about was how "find" normally traverses directories, which is exactly as shown above. However, find has a "-depth" flag and it had that in 2001. Given the same directory structure:

$ find . -depth -ls
62784225        0 drwxr-xr-x    2 tony             staff                  68 Aug 12 06:43 ./b/c/d/f
62784224        0 drwxr-xr-x    3 tony             staff                 102 Aug 12 06:43 ./b/c/d
62784223        0 drwxr-xr-x    3 tony             staff                 102 Aug 12 06:43 ./b/c
62784222        0 drwxr-xr-x    3 tony             staff                 102 Aug 12 06:43 ./b
62784234        0 -rw-r--r--    1 tony             staff                   0 Aug 12 06:44 ./c/d/f/file
62784228        0 drwxr-xr-x    3 tony             staff                 102 Aug 12 06:44 ./c/d/f
62784227        0 drwxr-xr-x    3 tony             staff                 102 Aug 12 06:43 ./c/d
62784226        0 drwxr-xr-x    3 tony             staff                 102 Aug 12 06:43 ./c
35965024        0 drwxr-xr-x    4 tony             staff                 136 Aug 12 06:43 .
 

Man pages for "find" may explain "-depth" with some variation of this:


This option is equivalent to the -depth primary of IEEE Std 1003.1-2001 (``POSIX.1''). The -d option can be useful when find is used with cpio(1) to process files that are contained in directories with unusual permissions. It ensures that you have write permission while you are placing files in a directory, then sets the directory's permissions as the last thing.

But to get bac(k) to "tac", the person who suggested that gave us this script:

find $1 -type d | tac |
        while read D
        do
                l $D | grep -q 'total 0' && rmdir $D
        done
 

But others, aware of "-depth", suggested

find . -d -type d -exec rmdir {} \;  
 

Of course that would spit out complaints:

$ find . -d -type d -exec rmdir {} \; 
rmdir: ./c/d/f: Directory not empty
rmdir: ./c/d: Directory not empty
rmdir: ./c: Directory not empty
rmdir: .: Invalid argument
 

You could send those to /dev/null, but "find . -type d -delete" does its work quietly.

But why does that work? There's no "-d" or "-depth"..

The "find" man page explains it:


-delete Delete found files and/or directories. Always returns true. This executes from the current working directory as find recurses down the tree. It will not attempt to delete a filename with a ``/'' character in its pathname relative to ``.'' for security reasons. Depth-first traversal processing is implied by this option. Following symlinks is incompatible with this option.

There, wasn't that fun?

Reference: Deleting empty directories



Got something to add? Send me email.





(OLDER)    <- More Stuff -> (NEWER)    (NEWEST)   

Printer Friendly Version

-> -> Removing empty directories




Increase ad revenue 50-250% with Ezoic


More Articles by

Find me on Google+

© Anthony Lawrence



Kerio Samepage


Have you tried Searching this site?

Unix/Linux/Mac OS X support by phone, email or on-site: Support Rates

This is a Unix/Linux resource website. It contains technical articles about Unix, Linux and general computing related subjects, opinion, news, help files, how-to's, tutorials and more.

Contact us





C++: NSStringWithoutYourMothersHatButWithNiceCandyFrosting - aargh! (Tony Lawrence)

If you ask "Should we be in space?" you ask a nonsense question. We are in space. We will be in space. (Frank Herbert)












This post tagged: