remover arquivo, mas excluir todos os arquivos em uma lista

16

Eu preciso limpar uma pasta periodicamente. Recebo uma lista de arquivos que contém texto, quais arquivos são permitidos. Agora eu tenho que excluir todos os arquivos que não estão neste arquivo.

Exemplo:

dont-delete.txt:

dontdeletethisfile.txt
reallyimportantfile.txt
neverdeletethis.txt
important.txt

Minha pasta de limpeza contém isso como exemplo:

ls /home/me/myfolder2tocleanup/:

dontdeletethisfile.txt
reallyimportantfile.txt
neverdeletethis.txt
important.txt
this-can-be-deleted.txt
also-waste.txt
never-used-it.txt

Portanto, esses arquivos devem ser excluídos:

this-can-be-deleted.txt
also-waste.txt
never-used-it.txt

Eu procuro algo para criar um comando de exclusão com uma opção para excluir alguns arquivos fornecidos por arquivo.

stefan83
fonte
Isso é um dever de casa?
precisa saber é o seguinte
Espero que você não seja o professor dele. lol
Gujarat Santana
2
@gujarat Não somos um serviço gratuito de lição de casa, portanto o comentário é justificado. Quanto à pergunta em si, ela pode ser útil para outras pessoas, por isso está aberta até o momento.
Sergiy Kolodyazhnyy 28/09
@Serg Estou concordo totalmente com você
Gujarat Santana

Respostas:

8

O rmcomando é comentado para que você possa verificar e verificar se está funcionando conforme necessário. Depois, desmarque essa linha.

A check directoryseção garantirá que você não execute o script acidentalmente do diretório errado e bloqueie os arquivos errados.

Você pode remover a echo deletinglinha para executar silenciosamente.

#!/bin/bash

cd /home/me/myfolder2tocleanup/

# Exit if the directory isn't found.
if (($?>0)); then
    echo "Can't find work dir... exiting"
    exit
fi

for i in *; do
    if ! grep -qxFe "$i" filelist.txt; then
        echo "Deleting: $i"
        # the next line is commented out.  Test it.  Then uncomment to removed the files
        # rm "$i"
    fi
done
LD James
fonte
Editei seu código para evitar o uso inútills e a captura inútil da saída, grepse tudo o que você deseja saber é se houve uma correspondência ou não. Eu também usei padrões de cadeia fixa para evitar problemas de escape.
David Foerster
@DavidFoerster Obrigado pela contribuição. No entanto, quando você alterou o whileloop para um forloop, alterou inadvertidamente o iteration keyde ipara f. na declaração, que quebrou o código. Eu consertei isso.
LD James
Opa, força do hábito. Eu tendem a abreviar nomes de variáveis ​​de shell para nomes de arquivos como f. ;-P (... e um para a sua resposta, que eu esqueci anteriormente.)
David Foerster
10

Este script python pode fazer isso:

#!/usr/bin/env python3
import os
no_remove = set()
with open('./dont-delete.txt') as f:
     for line in f:
         no_remove.add(line.strip())

for f in os.listdir('.'):
    if f not in no_remove:
        print('unlink:' + f ) 
        #os.unlink(f)

Parte importante é descomentar a os.unlink()função.

NOTA : adicione esse script e dont-delete.txtao seu dont-delete.txtpara que ambos estejam na lista e mantenha-os no mesmo diretório.

Sergiy Kolodyazhnyy
fonte
11
Alterei seu código para usar uma setlista em vez de uma lista para pesquisa O (1) em vez de O (n) na segunda parte.
David Foerster
obrigado pela vossa ajuda, eu sou normalmente uma janela de cara, mas costuras python também ser legal =)
stefan83
11
@ stefan83: O Python também funciona no Windows.
David Foerster
3

Aqui está uma frase:

comm -2 -3 <(ls) <(sort dont_delete) | tail +2 | xargs -p rm
  1. ls imprime todos os arquivos no diretório atual (em ordem classificada)
  2. sort dont_delete imprime todos os arquivos que não queremos excluir na ordem classificada
  3. a <() operador transforma uma string em um objeto parecido com um arquivo
  4. Os commcomandos comparam dois arquivos pré-classificados e imprimem linhas nas quais eles diferem
  5. o uso dos -2 -3sinalizadores faz commcom que apenas as linhas contidas no primeiro arquivo sejam impressas, mas não no segundo, que será a lista de arquivos seguros para excluir
  6. a tail +2chamada é apenas para remover o cabeçalho da commsaída, que contém o nome do arquivo de entrada
  7. Agora temos uma lista de arquivos a serem excluídos na saída padrão. Canalizamos essa saída para a xargsqual transformará o fluxo de saída em uma lista de argumentos para rm. A -popção obriga xargsa pedir confirmação antes de executar.
Gardenhead
fonte
Obrigado pela sua ajuda, agora tenho minha solução!
stefan83
@ Gardenhead, eu cansei seu código, mas ele remove todos os arquivos no diretório e mantém apenas o primeiro e o último arquivo na lista de não excluir. você tem alguma idéia para esse problema? desde já, obrigado.
Negar 07/07/19
1

Parece que você pode fazer isso nativamente zshusando o (+cmd)qualificador glob.

Para ilustrar, vamos começar com alguns arquivos

 % ls
bar  baz  bazfoo  keepfiles.txt  foo  kazoo

e um arquivo de lista de permissões

 % cat keepfiles.txt
foo
kazoo
bar

Primeiro, leia a lista de permissões em uma matriz:

 % keepfiles=( "${(f)$(< keepfiles.txt)}" )

ou talvez melhor

 % zmodload zsh/mapfile
 % keepfiles=( ${(f)mapfile[./keepfiles.txt]} )

(o equivalente ao mapfilebuilt-in do bash - ou seu sinônimo readarray). Agora podemos verificar se existe uma chave (nome do arquivo) na matriz usando o ${keepfiles[(I)filename]}que retorna 0 se nenhuma correspondência for encontrada:

 % print ${keepfiles[(I)foo]}
1
 % print ${keepfiles[(I)baz]}
0
 %

Podemos usar isso para criar uma função que retorna truese não houver correspondências $REPLYno array:

% nokeep() { (( ${keepfiles[(I)$REPLY]} == 0 )); }

Finalmente, usamos esta função como um qualificador em nosso comando:

 % ls *(+nokeep)
baz  bazfoo  keepfiles.txt

ou, no seu caso

 % rm -- *(+nokeep)

(Você provavelmente adicionará o nome do arquivo da lista de permissões à própria lista de permissões.)

chave de aço
fonte
0

Supondo que seu shell bash esteja ativado extglob shopt, aqui está uma alternativa um pouco mais conservadora:

rm !($(tr \\n \| < keep.txt))

(... acompanhando a excelente sugestão de comunicação de @ gardenhead!)

conny
fonte
0

Supondo que não haja espaço em branco (espaços / guias) nos seus arquivos listados em um arquivo chamado list, você faria:

find /path/to -type f \( ! -name "list" $(printf ' -a ! -name %s\n' $(< list)) \)

Basta adicionar -deleteao comando acima para excluir os arquivos que não existem no arquivo de lista . Se o seu achado não tem -deleteopção, você pode usar rmcom -execas seguintes:

find /path/to -type f \( ! -name "list" $(printf ' -a ! -name %s\n' $(< list)) \) -exec echo rm {} \;

Ou usando -execcom +terminador .

find /path/to -type f \( ! -name "list" $(printf ' -a ! -name %s\n' $(< list)) \) -exec echo rm {} +

echo é usado apenas para executar a seco.

αғsнιη
fonte
0

A menos que a saída ls /home/me/myfolder2tocleanup/exceda o limite máximo de argumentos do shell, ARG_MAX que é de cerca de 2 MB para o Ubuntu, sugiro o seguinte.


Uma implementação de comando de uma linha que fará o trabalho seria a seguinte:

  1. Copie o dont-delete.txtarquivo para o diretório que contém os arquivos a serem excluídos da seguinte maneira:
cp dont-delete.txt /home/me/myfolder2tocleanup/
  1. cd para o diretório que contém os arquivos a serem excluídos da seguinte forma:
cd /home/me/myfolder2tocleanup/
  1. Execute um teste a seco para testar o comando e fazer com que ele imprima os nomes dos arquivos que ele detecta para serem excluídos sem realmente excluí-los, da seguinte maneira:
ls -p | grep -v / | sed 's/\<dont-delete.txt\>//g' | sort | comm -3 - <(sort dont-delete.txt) | xargs echo | tr " " "\n"
  1. Se você estiver satisfeito com a saída, exclua os arquivos executando o comando da seguinte maneira:
ls -p | grep -v / | sed 's/\<dont-delete.txt\>//g' | sort | comm -3 - <(sort dont-delete.txt) | xargs rm

Explicação:

  • ls -plistará todos os arquivos e diretórios no diretório atual e a opção -padicionará um /aos nomes dos diretórios.
  • grep -v /excluirá diretórios removendo todos os itens que contêm um /em seus nomes.
  • sed 's/\<dont-delete.txt\>//g'excluirá o dont-delete.txtarquivo, para que ele não seja excluído no processo.
  • sortirá, apenas para ter certeza, classificar a saída restante de ls.
  • comm -3 - <(sort dont-delete.txt)irá classificar o dont-delete.txtarquivo, compará-lo com a saída classificada lse excluir nomes de arquivos existentes em ambos.
  • xargs rmremoverá todos os nomes de arquivos restantes na saída já processada de ls. Isso significa que todos os itens no diretório atual serão removidos, exceto os diretórios , os arquivos listados no dont-delete.txtarquivo e o dont-delete.txtpróprio arquivo

Na parte seca:

  • xargs echo imprimirá os arquivos que devem ser removidos.
  • tr " " "\n" traduzirá espaços em novas linhas para facilitar a legibilidade.
Raffa
fonte
-1

Minha sugestão é:

sed -e 's/^/\.\//' dont-delete.txt > dont-delete-relative-path.txt
find . -type f -print | grep -Fxvf dont-delete-relative-path.txt | xargs -d'\n' rm

Atualização 2018-08-07

Exemplo:

1: mkdir /tmp/delete-example && cd /tmp/delete-example
2: touch a b c d
3: echo "./a\n./b\n./dont-delete.txt\n" > dont-delete.txt
4: find . -type f -print | grep -Fxvf dont-delete.txt | xargs -d'\n' rm

Observe que após a linha 3 você terá o dont-delete.txtarquivo com o conteúdo:

./a
./b
./dont-delete.txt

(a liderança ./é muito importante )

Os arquivos ce dserão excluídos.

nyxz
fonte
Eu tentei isso com um arquivo de texto com os nomes dos arquivos separados por uma nova linha. Acabou excluindo todos os arquivos no diretório.
Jacques MALAPRADE
Eu acho que sua "lista de manutenção" estava errada.
Nyxz 7/08
Adicionei exemplo de uso.
Nyxz