Ubuntu – Delete the parent directory (non-empty) if a specific child directory is empty

command linedirectoryfind

I have to keep all the directory which contains files in a specific subdirectory but to delete rest all directories in which subdirectory is empty.

To be more specific here is the structure:

A
|
|--------312311
|       |
|       |----Recording
|       |----a.txt
|       |----b.txt
|
|
|--------453453
|       |----Recording
|                   |
|                   |-------a.mp3
|       |----a.txt
|       |----b.txt
|
|
|--------566532
|       |----Recording
|       |----a.txt
|       |----b.txt

The subdirectories may or may not contain a file. So I need to delete the whole directory like '312311' & '566532' and only '453453' should be left with all data in it because it has a file in 'Recording' folder which is a specific directory for me.

I saw many posts but it links to many specific file names. Any help will be much appreciated as I need to do many times in a week.

Best Answer

The script below will do exactly as you describe, it:

  1. lists the folders inside a directory
  2. Looks inside each of the folders for a folder named "Recording"

    • If it exists and is empty, it deletes its superior folder
    • if it does not exist, it also deletes its superior folder
    • files on the first level inside A will not be deleted.

In an image:

A
|
|--------123456
|       |
|       |----Recording
|       |----a.txt
|       |----b.txt
|
|
|--------635623
|       |----Recording
|                   |
|                   |-------a.mp3
|       |----a.txt
|       |----b.txt
|
|
|--------123456
|       |----Recording
|       |----a.txt
|       |----b.txt
|
|--------Monkey.txt

will result in:

A
|
|
|--------635623
|       |----Recording
|                   |
|                   |-------a.mp3
|       |----a.txt
|       |----b.txt
|
|
|--------Monkey.txt

The script

#!/usr/bin/env python3
import os
import sys
import shutil

dr = sys.argv[1]

def path(*args):
    return os.path.join(*args)

for d in os.listdir(dr):
    try:
        if not os.listdir(path(dr, d, "Recording")):
            shutil.rmtree(path(dr,d))
    except FileNotFoundError:
        shutil.rmtree(path(dr,d))
    except NotADirectoryError:
        pass

To use

  1. Copy the script into an empty file, save it as delete_empty.py
  2. Run it with the (full!) directory (containinf your subdirs, A in your example) as argument by the command:

    python3 /path/to/delete_empty.py /path/to/directory
    

That's it.

Explanation

Feeding the content of your folder "A" to the script,

os.listdir(dr)

will list its subdirectories (and files). Then:

if not os.listdir(path(dr, d, "Recording"))

will try to list the content of each of the (sub)folders, which will raise an error if the item is a file:

except NotADirectoryError
    pass

or if the folder "Recording" does not exist at all:

FileNotFoundError
    shutil.rmtree(path(dr,d))

If the folder "Recording" exists and is empty, the superior folder is removed:

if not os.listdir(path(dr, d, "Recording")):
    shutil.rmtree(path(dr,d))

EDIT

Additionally, as requested in comments, a version that will check for multiple subdirs (names).

In case the directory contains any of the listed (un- empty) subdirs, the directory is kept. Else it will be deleted.

To use

  1. Copy the script into an empty file, save it as delete_empty.py
  2. Run it with the (full!) directory (containing your subdirs, A in your example) and the names of subdirs as arguments by the command:

    python3 /path/to/delete_empty.py /path/to/directory <subdir1> <subdir2> <subdir3>
    

That's it.

The script

#!/usr/bin/env python3
import shutil
import os
import sys

dr = sys.argv[1]; matches = sys.argv[2:]

def path(*args):
    return os.path.join(*args)

for d in os.listdir(dr):
    # delete directory *unless* either one of the listed subdirs has files
    keep = False
    # check for each of the listed subdirs(names)
    for name in matches:
        try:
            if os.listdir(path(dr, d, name)):
                keep = True
                break
        except NotADirectoryError:
            # if the item is not a dir, no use for other names to check
            keep = True
            break
        except FileNotFoundError:
            # if the name (subdir) does not exist, check for the next
            pass
    if not keep:
        # if there is no reason to keep --> delete
        shutil.rmtree(path(dr,d))

Note

Please first run on a test directory to make sure it does exactly what you want.