Exclua o diretório pai (não vazio) se um diretório filho específico estiver vazio

11

Eu tenho que manter todo o diretório que contém arquivos em um subdiretório específico, mas excluir todos os diretórios nos quais o subdiretório está vazio.

Para ser mais específico, aqui está a imagem:

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

Os subdiretórios podem ou não conter um arquivo. Portanto, preciso excluir o diretório inteiro como '312311' & '566532' e apenas '453453' devem ser deixados com todos os dados contidos nele bcoz tem um arquivo na pasta 'Recording' que é um diretório específico para mim.

Eu vi muitos posts, mas ele está vinculado a muitos nomes de arquivos específicos. Qualquer ajuda será muito apreciada como eu preciso fazer muitas vezes em uma semana.

    
por Derek James 18.03.2017 / 12:55

3 respostas

12

O script abaixo fará exatamente o que você descreve:

  1. lista as pastas dentro de um diretório
  2. Olha dentro de cada uma das pastas para uma pasta chamada "Gravação"

    • Se existir e estiver vazio, elimina a sua pasta superior
    • se não existir, também exclui sua pasta superior
    • arquivos no primeiro nível dentro de A não serão excluídos.

Em uma imagem:

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

resultará em:

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

O 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

Para usar

  1. Copie o script em um arquivo vazio, salve-o como delete_empty.py
  2. Execute-o com o diretório (full!) (contendo seus subdiretórios, A no seu exemplo) como argumento pelo comando:

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

É isso aí.

Explicação

Alimentando o conteúdo da sua pasta "A" ao script,

os.listdir(dr)

listará seus subdiretórios (e arquivos). Então:

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

tentará listar o conteúdo de cada uma das (sub) pastas, o que gerará um erro se o item for um arquivo:

except NotADirectoryError
    pass

ou se a pasta "Gravação" não existir de todo:

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

Se a pasta "Gravação" existir e estiver vazia, a pasta superior será removida:

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

EDITAR

Além disso, conforme solicitado nos comentários, uma versão que verificará vários subdiretórios (nomes).

Caso o diretório contenha qualquer dos subdiretórios listados (não vazios), o diretório será mantido. Senão será deletado.

Para usar

  1. Copie o script em um arquivo vazio, salve-o como delete_empty.py
  2. Execute-o com o diretório (full!) (contendo seus subdiretórios, A no seu exemplo) e os nomes dos subdiretórios como argumentos pelo comando:

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

É isso aí.

O 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))

Nota

Primeiramente, execute em um diretório de teste para garantir que ele faça exatamente o que você deseja.

    
por Jacob Vlijm 18.03.2017 / 13:28
11

Usando find e xargs :

find A -type d -name 'Recording' -empty -printf '%h
       %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
              ".".
' | xargs -0 echo rm -rf

(remova o echo quando estiver confortável em identificar os diretórios corretos). A parte printf '%hman find imprime o diretório pai (terminado com nulo) - de %code% :

$ 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

Ex .: dado

$ find A -type d -name 'Recording' -empty -printf '%h
find A -type d -name 'Recording' -empty -printf '%h
       %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
              ".".
' | xargs -0 echo rm -rf
' | xargs -0 rm -rf $ $ tree A A └── 453453 ├── a.txt ├── b.txt └── Recording └── a.mp3 2 directories, 3 files

então

$ 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
    
por steeldriver 18.03.2017 / 13:32
3

Aqui está uma solução mais simples:

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

Funciona porque rmdir falhará se o diretório não estiver vazio e && impedirá que rm -r seja executado, a menos que rmdir seja bem-sucedido. O glob */ garante que você está trabalhando apenas nos diretórios, portanto, outros arquivos não serão afetados.

    
por JoL 19.03.2017 / 20:03