Удалить родительский каталог (не пустой), если конкретный дочерний каталог пуст

11

Я должен сохранить все каталоги, которые содержат файлы в определенном подкаталоге, но удалить остальные каталоги, в которых подкаталог пуст.

Чтобы быть более конкретным, вот структура:

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

Подкаталоги могут содержать или не содержать файл. Поэтому мне нужно удалить весь каталог, например «312311» и «566532», и нужно оставить только «453453» со всеми данными в нем, поскольку в нем есть файл в папке «Запись», который является для меня специальным каталогом.

Я видел много постов, но это ссылки на многие конкретные имена файлов. Любая помощь будет высоко ценится, так как мне нужно делать много раз в неделю.

Дерек Джеймс
источник
Всегда ли запись на первом уровне в каталоге? Также: все ли папки на первом уровне внутри основного каталога?
Джейкоб Влейм
A - каталог верхнего уровня, в котором он содержит пронумерованные каталоги. А в пронумерованных каталогах он содержит папку «Recording», а также некоторые «txt» файлы, как, например, в. И в «Записи» он может содержать или не содержать файлы ..
Дерек Джеймс
Удалите ли вы изображение из вашего текста и поместите фактический текст в ваш вопрос. Картинки с текстом громоздки для обработки. Использование реального текста значительно удобнее, чем другие присущие ему удобства.
Л.Д. Джеймс
Я сделал то же самое, что вы спросили, Джеймс ..
Дерек Джеймс

Ответы:

12

Сценарий ниже будет делать именно так, как вы описываете, это:

  1. перечисляет папки внутри каталога
  2. Заглядывает в каждую из папок для папки с именем "Запись"

    • Если он существует и пуст, он удаляет свою верхнюю папку
    • если он не существует, он также удаляет свою верхнюю папку
    • файлы на первом уровне внутри A не будут удалены.

На изображении:

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

приведет к:

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

Сценарий

#!/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

Использовать

  1. Скопируйте скрипт в пустой файл, сохраните его как delete_empty.py
  2. Запустите его с каталогом (full!) (Содержащим ваши подкаталоги, A в вашем примере) в качестве аргумента команды:

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

Вот и все.

объяснение

Подача содержимого вашей папки «А» в скрипт,

os.listdir(dr)

перечислит свои подкаталоги (и файлы). Потом:

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

попытается перечислить содержимое каждой из (под) папок, что вызовет ошибку, если элемент является файлом:

except NotADirectoryError
    pass

или если папка «Запись» вообще не существует:

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

Если папка «Запись» существует и пуста, верхняя папка удаляется:

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

РЕДАКТИРОВАТЬ

Кроме того, как требуется в комментариях, версия, которая будет проверять наличие нескольких подкаталогов (имен).

Если каталог содержит какой-либо из перечисленных (не пустых) подкаталогов, каталог сохраняется. В противном случае он будет удален.

Использовать

  1. Скопируйте скрипт в пустой файл, сохраните его как delete_empty.py
  2. Запустите его с каталогом (full!) (Содержащим ваши подкаталоги, A в вашем примере) и именами подкаталогов в качестве аргументов команды:

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

Вот и все.

Сценарий

#!/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))

Заметка

Пожалуйста, сначала запустите тестовый каталог, чтобы убедиться, что он делает именно то, что вы хотите.

Якоб Влейм
источник
этот код действительно помог, и он работал как сказано. Спасибо за код. Действительно полезно.
Дерек Джеймс
Предположим, если мне нужно сохранить и проверить две папки или более, как я смогу сделать в этом коде? добавив 'ИЛИ' или трубу '| 'где-то .. предложить что-то!
Дерек Джеймс
Привет @DerekJames, который требует только незначительного редактирования, я выложу сегодня днем.
Джейкоб Влейм
@ Дерек, подожди, должна ли быть удалена верхняя папка, если какая-либо из папок пуста, или все? Может ли их быть несколько в одном старшем каталоге?
Джейкоб Влейм
Предположим, @Jacob, у меня есть две папки «Запись» и «Видео». Следует проверить оба. Если какая-либо из подпапок содержит файл, папка «635623» не должна быть удалена. А также возможность добавлять больше папок, чтобы проверить, можно ли это сделать ..
Дерек Джеймс
11

Использование findи xargs:

find A -type d -name 'Recording' -empty -printf '%h\0' | xargs -0 echo rm -rf

(удалите, echoкогда вам удобно, что он определяет правильные каталоги). printf '%h\0Часть печатает (нуль) родительский каталог - от 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
              ".".

Пример: дано

$ 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

тогда

$ 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
steeldriver
источник
найти A -тип d -name 'Запись' -empty -printf '% h \ 0' | xargs -0 echo rm -rf это просто выводит "rm -rf" и находит A -type d -name 'Recording' -empty -printf '% h \ 0' | xargs -0 rm -rf не работает ... Я что-то не так делаю ..
Дерек Джеймс
@DerekJames вам нужно заменить A на фактический каталог.
Джейкоб Влейм
Было бы хорошо поставить регулярное выражение вместо имени записи, так как OP давал имена файлов с числами, скажем [\d]*?
Джордж Удосен
@JacobVlijm: я заменил реальный каталог, но все еще не работает?
Дерек Джеймс
@DerekJames, это странно! это работало в моей системе, но вы, возможно, забыли сначала перейти в каталог? Ожидание, это не должно иметь значения ...
Джейкоб Влейм
3

Вот более простое решение для bash:

for d in */; do rmdir "$d/Recording" && rm -r "$d"; done

Это работает, потому rmdirчто потерпит неудачу, если каталог не пустой, и &&предотвратит rm -rвыполнение, если это не rmdirудастся. Шар */гарантирует, что вы работаете только с каталогами, поэтому другие файлы не будут затронуты.

Йол
источник
Умное мышление! +1
Джейкоб Влейм