Exclui todos os arquivos nos diretórios, exceto aqueles cujo caminho está listado em um arquivo

6

Dados os seguintes arquivos:

data/A/a.txt
data/B/b.pdf
...
date/P/whatever.log
...
data/Z/z.jpg

Eu gostaria de excluir todos os arquivos nos diretórios data/A/ , data/B/ , ..., data/Z/ exceto os arquivos que estão situados em um dos diretórios listados no arquivo %código%. Por exemplo, se tivermos data/dont_clean.txt listado em data/P , nada deverá ser tocado em data/dont_clean.txt , etc.

Algo como:

find data/ -mindepth 2 -maxdepth 2 -type f -not -path {listed in data/dont_clean} -delete

Claro que não é um comando válido.

Eu também tentei variantes de

find data/ -mindepth 2 -maxdepth 2 -type f -exec grep data/dont_clean.txt '{}' \;

mas eu criei apenas um comando inválido ou não sabia por que obtive a saída que fiz.

Estou usando o bash no Ubuntu 12.10

    
por Ali 11.10.2013 / 21:35

6 respostas

3

Esse é um código que eu apenas testei grosseiramente, mas que poderia ter uma abordagem para você. Supondo que você tenha um arquivo, ignore.txt assim:

1/
2/

Dados de amostra

E eu tinha diretórios de amostra com arquivos neles assim:

$ mkdir -p dirs/{1..5}
$ touch dirs/{1..5}/afile

Resultando nisso:

$ tree dirs/
dirs/
|-- 1
|   '-- afile
|-- 2
|   '-- afile
|-- 3
|   '-- afile
|-- 4
|   '-- afile
'-- 5
    '-- afile

Exemplo de execução

Agora, se executarmos este comando nessa árvore:

$ find dirs/ -type f -print0 | fgrep -zFvf ./ignore.txt
dirs/5/afiledirs/4/afiledirs/3/afile

Podemos ver que estamos recuperando apenas os arquivos que estão nos diretórios não listados em ignore.txt .

Portanto, podemos adicionar um rm ao final para remover os arquivos não excluídos.

$ find dirs/ -type f -print0 | fgrep -zFvf ./ignore.txt | xargs -0 rm -f

Verificamos que podemos ver que funcionou:

$ tree dirs/
dirs/
|-- 1
|   '-- afile
|-- 2
|   '-- afile
|-- 3
|-- 4
'-- 5

Problemas a serem resolvidos

Um grande problema com essa abordagem é que as sequências no arquivo ignore.txt podem corresponder a outras partes da estrutura de diretórios. Portanto, alguns cuidados devem ser pagos para garantir que as strings neste arquivo sejam exclusivas da maneira esperada.

Alguns bloqueios podem ser colocados em torno das strings de modo que estejam ancorados no começo ou no final da string para protegê-los.

Detalhes

Os comandos acima estão fazendo o seguinte:

  1. localizando todos os arquivos no diretório dirs
  2. filtrando todos os arquivos que estão sob um diretório presente no arquivo igonre.txt
  3. passando a lista de filtros via xargs para o comando rm -f
por 11.10.2013 / 22:03
1

Parece um caso para o comando comm .

list of files to not delete in "keeper"
ls >current
comm -23 current ../keeper | more

verifique se é a lista correta

comm -23 current ../keeper | xargs rm
    
por 11.10.2013 / 23:03
0

xargs e find combinação

Demo:

Meus arquivos:

[root@mail tmp]# find data/ -type f
data/A/d.txt
data/A/b.txt
data/A/a.txt
data/A/c.txt
data/B/e.txt
data/B/g.txt
data/B/f.txt
data/B/i.txt

Lista de exclusões

[root@mail tmp]# cat exclude 
data/A/a.txt
data/B/e.txt

encontre com xargs

[root@mail tmp]# find data/ -type f $( xargs  -I{} echo -n " -not -path {} " < exclude )
data/A/d.txt
data/A/b.txt
data/A/c.txt
data/B/g.txt
data/B/f.txt
data/B/i.txt

Parece que a Saída está OK, vamos excluir, mas antes disso, certifique-se de que a saída do comando a seguir seja OK e, em seguida, você pode remover o segundo último comando echo .

[root@mail tmp]# find data/ -type f $( xargs  -I{} echo -n " -not -path {} " < exclude ) | xargs -n1 echo rm -rf
rm -rf data/A/d.txt
rm -rf data/A/b.txt
rm -rf data/A/c.txt
rm -rf data/B/g.txt
rm -rf data/B/f.txt
rm -rf data/B/i.txt
    
por 11.10.2013 / 22:33
0

A única coisa que precisa ser resolvida é criar um programa que:

  • lê uma linha do stdout,

  • ecoa essa linha se não começar com nenhuma das strings listado no arquivo data/dont_clean.txt .

Eu não consegui resolver isso com um script bash facilmente, então escrevi um pequeno programa em C ++ chamado my_program que faz exatamente isso. Então eu consigo o que eu quero, se eu corro:

find data/ -mindepth 2 -maxdepth 2 -type f | my_program | xargs rm -f 

No entanto, deixo essa questão ainda em aberto, ainda estou interessado em uma solução pura de shellscript.

    
por 12.10.2013 / 01:09
0

Você pode fazer isso em duas etapas: marcar (para diferenciá-las) e, em seguida, remover desprotegido.

Se todos esses arquivos forem do mesmo usuário, você poderá usar a lista para alterar a propriedade ou o grupo para outro usuário. Em seguida, use find para remover o resto e, em seguida, altere o usuário de volta. Se todos os arquivos tiverem as mesmas permissões de leitura (para que você saiba como voltar), você pode, por exemplo, remover a permissão de leitura (para sinalizá-los) porque é muito mais fácil do que mudar de usuário (sem privilégios de root):

while read file; do chmod u-w -R "$file"; done < "data/dont_clean.txt"
find data/ -mindepth 2 -maxdepth 2 -type f -writable -delete
while read file; do chmod u+w -R "$file"; done < "data/dont_clean.txt"

Editado (recursão adicionada) para proteger diretórios inteiros. Mudou de leitura para gravação de permissões para evitar problemas com a recursão do chmod (não foi possível ler o diretório que ele acabou de alterar).

    
por 09.03.2014 / 11:54
0

Você pode processar data/dont_clean.txt em uma lista de globs a serem ignorados por bash via GLOBIGNORE , por exemplo. se dont_clean.txt contiver caminhos de diretório como

data/P
data/some_dir

o resultado seria GLOBIGNORE=data/P/*:data/some_dir/* ;
você então removeria todos os arquivos regulares que o glob data/*/* expande para:

GLOBIGNORE=$(sed -n 's|$|/*|;H;1h;$x;s|\n|:|gp' data/dont_clean.txt)
for f in data/*/*; do [[ -f "$f" ]] && rm "$f"; done
unset GLOBIGNORE

O acima pressupõe nomes de diretórios sensatos na sua lista. Use printf %s\n em vez de rm se você quiser ver quais arquivos serão removidos.

    
por 09.12.2015 / 22:26