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 em que o subdiretório está vazio.

Para ser mais específico aqui é a estrutura:

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

Os subdiretórios podem ou não conter um arquivo. Então, eu preciso excluir o diretório inteiro como '312311' e '566532' e apenas '453453' deve ser deixado com todos os dados, pois ele possui um arquivo na pasta 'Recording', que é um diretório específico para mim.

Eu vi muitos posts, mas ele tem links para muitos nomes de arquivos específicos. Qualquer ajuda será muito apreciada, como eu preciso fazer várias vezes em uma semana.

Derek James
fonte
A gravação está sempre no primeiro nível no diretório? Além disso: todas as pastas estão no primeiro nível dentro do diretório principal?
Jacob Vlijm 18/03/19
A é o diretório de nível superior no qual contém diretórios numerados. E nos diretórios numerados, ele contém a pasta 'Recording', bem como alguns arquivos 'txt', como em por exemplo. E em 'Gravação', ele pode ou não conter arquivos ...
Derek James
Você removerá a imagem do seu texto e colocará o texto real na sua pergunta. Imagens de texto são complicadas para nós processarmos. Usar o texto real é substancialmente mais conveniente, entre outras conveniências inerentes.
LD James
Eu fiz o mesmo perguntou por você James ..
Derek James

Respostas:

12

O script abaixo fará exatamente como você descreve:

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

    • Se existir e estiver vazio, ele excluirá sua pasta superior
    • se não existir, também excluirá 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

vai 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

Usar

  1. Copie o script em um arquivo vazio, salve-o como delete_empty.py
  2. Execute-o com o diretório (completo!) (Contenha 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" para o 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:

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 procurará vários subdiretórios (nomes).

Caso o diretório contenha qualquer um dos subdiretórios listados (não vazios), o diretório será mantido. Caso contrário, ele será excluído.

Usar

  1. Copie o script em um arquivo vazio, salve-o como delete_empty.py
  2. Execute-o com o diretório (completo!) (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

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

Jacob Vlijm
fonte
esse código realmente ajudou e funcionou como foi dito. Obrigado pelo código. Realmente util.
Derek James
Suponha que, se eu precisar manter e verificar duas pastas ou mais, como poderei fazer esse código? adicionando 'OR' ou pipe '| 'em algum lugar .. sugerir algo!
Derek James
Olá @DerekJames, que requer apenas uma edição menor, vou postar esta tarde.
Jacob Vlijm
@ Derek, espere, a pasta superior deve ser removida se alguma das pastas estiver vazia ou todas? Poderia haver vários deles em um dir superior?
Jacob Vlijm 27/03
Suponha que @Jacob, eu tenho duas pastas 'Recording' And 'Video'. Deve verificar os dois. Se alguma das subpastas contiver o arquivo, a pasta '635623' não será excluída. E também a capacidade de adicionar mais pastas para verificar se isso pode ser feito.
Derek James
11

Usando finde xargs:

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

(remova a opção echoque estiver identificando os diretórios corretos). A printf '%h\0peça imprime o diretório-pai (terminado em nulo) - de 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 .: dado

$ 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

então

$ 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
chave de aço
fonte
localize A -type d -name 'Recording' -vazty -printf '% h \ 0' | xargs -0 echo rm -rf isto está apenas imprimindo "rm -rf" E encontre A -type d -name 'Recording' -empty -printf '% h \ 0' | xargs -0 rm-rf não está funcionando ... Estou fazendo algo errado ..
Derek James
@DerekJames, você precisa substituir A pelo diretório real.
Jacob Vlijm
Seria bom colocar uma expressão regular no lugar do nome da gravação, já que o OP forneceu nomes de arquivos com números, digamos [\d]*?
George Udosen
@JacobVlijm: Substituí pelo diretório real, mas ainda não estou funcionando?
Derek James
@DerekJames que é estranho! funcionou no meu sistema, mas você pode ter esquecido de entrar primeiro no diretório? Waitwait, que não deve importar ...
Jacob Vlijm
3

Aqui está uma solução mais simples do bash:

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

Funciona porque rmdirfalhará se o diretório não estiver vazio e &&impedirá a rm -rexecução, a menos que seja rmdirbem-sucedido. A glob */garante que você esteja trabalhando apenas nos diretórios, para que outros arquivos não sejam afetados.

JoL
fonte
Pensamento inteligente! +1
Jacob Vlijm