Delete the parent directory (non-empty) if a specific child directory is empty
The script below will do exactly as you describe, it:
- lists the folders inside a directory
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
- Copy the script into an empty file, save it as
delete_empty.py
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
- Copy the script into an empty file, save it as
delete_empty.py
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.
Using find
and xargs
:
find A -type d -name 'Recording' -empty -printf '%h\0' | xargs -0 echo rm -rf
(remove the echo
once you are comfortable that it is identifying the correct directories). The printf '%h\0
part prints the (null terminated) parent directory - from man find
:
%h Leading directories of file's name (all but the last ele‐
ment). If the file name contains no slashes (since it is
in the current directory) the %h specifier expands to
".".
Ex.: given
$ tree A
A
├── 312311
│ ├── a.txt
│ ├── b.txt
│ └── Recording
├── 453453
│ ├── a.txt
│ ├── b.txt
│ └── Recording
│ └── a.mp3
└── 566532
├── a.txt
├── b.txt
└── Recording
6 directories, 7 files
then
$ find A -type d -name 'Recording' -empty -printf '%h\0' | xargs -0 rm -rf
$
$ tree A
A
└── 453453
├── a.txt
├── b.txt
└── Recording
└── a.mp3
2 directories, 3 files
Here is a simpler bash solution:
for d in */; do rmdir "$d/Recording" && rm -r "$d"; done
It works because rmdir
will fail if the directory is not empty, and &&
will prevent the rm -r
from being executed unless rmdir
succeeds. The glob */
makes sure you're only working on the directories, so other files will not be affected.