Exclua todos os arquivos em um diretório cujo nome não corresponde a uma linha em uma lista de arquivos

9

Eu tenho um diretório com mais de 1000 arquivos. Em um arquivo de texto, tenho cerca de 50 nomes de arquivos, um por linha. Eu gostaria de excluir todos os arquivos no diretório cujos nomes de arquivos não correspondem a uma entrada na lista. Qual é a melhor forma de fazer isso? Iniciei um script de shell, mas não foi possível determinar o comando adequado a ser determinado no nome do arquivo que está na lista. Obrigado.

Nathan
fonte

Respostas:

8

Sei que qualquer pergunta perguntando como excluir arquivos deve ser feita com muito cuidado. Minha primeira resposta foi muito apressada. Não aceitei o fato de que a lista de arquivos poderia estar malformada para ser usada com o egrep. Eu editei a resposta para reduzir esse risco.

Isso deve funcionar para os arquivos que não têm espaço no nome:

Primeiro recrie sua lista de arquivos para garantir o nome exato do arquivo:

sed -e 's,^,^,' -e 's,$,$,'  filelist  > newfilelist 

construir os comandos rm

cd your_directory
ls | egrep -vf newfilelist   | xargs -n 1 echo rm  >  rmscript

Verifique se o script rm combina com você (você pode fazê-lo com "vim" ou "less").
Em seguida, execute a ação:

sh -x rmscript

Se os arquivos tiverem espaços em seu nome (se os arquivos tiverem "o nome, isso não funcionará):

ls | egrep -vf newfilelist  | sed 's,^\(.*\)$,rm "\1",' > rmscript

é claro que a lista de arquivos não deve estar no mesmo diretório!

EDITADO:

A lista de arquivos do Nathan continha nomes que correspondiam a todos os arquivos no diretório (como "html" corresponde a "bob.html"). Portanto, nada foi excluído porque egrep -vfabsorveu todo o fluxo. Eu adicionei um comando para colocar um "^" e um "$" em torno de cada nome de arquivo. Eu tive sorte aqui que a lista de arquivos de Nathan estava correta. Se o DOS estivesse formatado com linhas terminadas CR-LF ou com espaços adicionais, nenhum arquivo seria preservado pelo egrep e todos seriam excluídos.

Emmanuel
fonte
Quando executo o comando preview, recebo uma linha com "rm". Quando executo o comando real, recebo uma mensagem de erro sobre argumentos ausentes para rm. Preciso de sintaxe especial para usar os resultados de ls | egrep na entrada xargs?
31414 Nathan
@ Nathan, você deve primeiro cd para o seu diretório. Sem sintaxe especial. lsfornece os nomes dos arquivos de diretório, egrep -vf filelistfiltre seus 50 nomes de arquivos. Receio que você excluiu todos os seus arquivos.
Emmanuel
@ Emamanuel Estou executando o comando no diretório que contém os arquivos a serem excluídos.
31414 Nathan
@ Nathan são todos os seus arquivos excluídos?
Emmanuel
não, eles ainda estão lá.
31414 Nathan
1

Pré-construa os argumentos para find:

{
  read -r
  keep=( -name "$REPLY" ) # no `-o` before the first one.
  while read -r; do
    keep+=( -o -name "$REPLY" )
  done
} < file_list.txt
find . -type f ! \( "${keep[@]}" \) -exec echo rm {} +

Use as echopeças para ver o que seria construído. Remova as echopeças para realmente executá-lo.

Atualização: Demonstração:

##
# Demonstrate what files exist for testing.
# Show their whitespace:
~/foo $ printf '"%s"\n' *
" op"
" qr"
"abc"
"def"
"gh "
"ij "
"k l"
"keep"
"m n"

##
# Show the contents of the "keep" file,
# Including its whitespace:
~/foo $ cat -e keep
keep$
abc$
gh $
k l$
 op$

##
# Execute the script:
~/foo $ { read -r; keep=( -name "$REPLY" ); while read -r ; do keep+=( -o -name "$REPLY" ); done } < keep
~/foo $ find . -type f ! \( "${keep[@]}" \) -exec rm {} +

##
# Show what files remain:
~/foo $ printf '"%s"\n' *
" op"
"abc"
"gh "
"k l"
"keep"
kojiro
fonte
Eu gosto deste melhor, pois elimina a necessidade filelist tona
eyoung100
+1 de mim, embora não lide muito bem com espaços. Talvez algumas aspas simples ( ') devam ser adicionadas, ie keep=( -name \'"$REPLY"\' )e keep+=( -o -name \'"$REPLY"\' ).
Cristian Ciupitu
o acima é perigoso, porque você pode excluir acidentalmente arquivos.
Davidva 26/08/14
@CristianCiupitu não é? Eu adicionei uma demonstração mostrando que ele lida muito bem com espaços em branco.
Kojiro 26/08/14
@davidva Sob que circunstâncias? Sempre que você automatiza a exclusão de coisas, corre o risco de cometer um erro, mas dentro dos parâmetros da pergunta, acho que minha demonstração prova que essa abordagem é sólida.
Kojiro # 26/14
1

Com zsh:

mylist=(${(f)"$(<filelist)"})
print -rl -- *(.^e_'(($mylist[(Ie)$REPLY]))'_)

Ele lê as linhas de filelistem uma matriz e, em seguida, usa qualificadores / estring glob para selecionar / selecionar apenas os nomes de arquivos que não estão presentes na matriz: .seleciona apenas arquivos regulares (adicione Dse sua lista contém arquivos de ponto) e o negado ^e_'expression'_ainda seleciona apenas aqueles para que a expressão retorna false, ou seja, se o nome ( $REPLY) não for um elemento da matriz .
Se você estiver satisfeito com o resultado, substitua print -rlpor rmpara remover os arquivos:

rm -- *(.^e_'(($mylist[(Ie)$REPLY]))'_)

Para selecionar e remover arquivos recursivamente, use o */**glob com ${REPLY:t}modificador glob:

rm -- */**(.^e_'(($mylist[(Ie)${REPLY:t}]))'_)
don_crissti
fonte
0

Se você colocar o conteúdo do diretório em um arquivo como este:

cd <somedirectory>
ls >> filelist

Abra a lista de arquivos com um editor de texto e remova todos os arquivos, exceto os que você deseja excluir . Está em negrito porque é a abordagem oposta à resposta acima

Tente o seguinte:

while read p || [[ -n $p ]]; 
echo $p
done < filelist

Se você vir sua lista de arquivos enviados para a tela, substitua eco rm -vpor:

while read p || [[ -n $p ]]; 
rm -v $p
done < filelist
eyoung100
fonte
0

Execute o script abaixo.

  1. Inicialmente, estou encontrando todos os arquivos presentes no diretório e armazenando a saída em outro arquivo all_files.
  2. Temos um arquivo que contém a lista de arquivos que NÃO devem ser excluídos ( not_to_be_deleted_files).
  3. Estou adicionando os nomes dos arquivos not_to_be_deleted_filese, files_to_be_deletedao final not_to_be_deleted_files, precisamos desses 2 arquivos.
  4. Agora, estou encontrando os arquivos que precisam ser excluídos usando o joincomando linux e redirecionando a saída para o files_to_be_deleted arquivo.
  5. Agora, no loop while final, estou lendo todos os nomes de arquivos files_to_be_deletede removendo os arquivos mencionados nesse nome.

O script é como abaixo.

find /home/username/directory -type f | sed 's/.*\///' > all_files
echo all_files >> not_to_be_deleted_files
echo not_to_be_deleted_files >> not_to_be_deleted_files
echo files_to_be_deleted >> not_to_be_deleted_files
join -v 1 <(sort all_files_listed) <(sort files_not_to_be_deleted) >   files_to_be_deleted
while read file
rm  "$file"
done < files_to_be_deleted

PS : Provavelmente, se você deseja que isso seja salvo como um script e executá-lo, você pode adicionar o nome do script também usando echo scriptname >> not_to_be_deleted_files.

Embora não seja obrigatório, prefiro fazê-lo porque não haverá arrependimentos mais tarde. Eu testei para um pequeno conjunto de arquivos e funcionou no meu sistema. No entanto, se você quiser ter certeza, tente testprimeiro um diretório e remova os arquivos no diretório original.

Ramesh
fonte
0
  • Use a lista como fonte, para mover todos os arquivos da lista para um novo diretório de salvamento novo, vazio e vazio.
  • Compare o número de arquivos na lista e o número de arquivos salvos.
  • Se ambos corresponderem, exclua todos os arquivos não salvos com o seu método favorito.
  • Mova os arquivos salvos de volta.
Usuário desconhecido
fonte
0

Fui para uma abordagem mais segura e muito, muito mais rápida porque tinha 18.000 arquivos na lista! Eu precisava limpar as imagens em uma instalação grande do Drupal.

Excluir todos os arquivos que não estão na lista é o mesmo que manter apenas os que estão na lista. Então, decidi copiar os arquivos da lista para outro local, mas copiar 20 GB de arquivos ocuparia muito espaço e também seria muito lento. Portanto, o truque é copiar os arquivos como alternativa hardlinks, usando a -lopção de cp. Isso ocupa quase nenhum espaço e é muito rápido. Além disso, como eu precisava preservar a estrutura de diretórios, usei a --parentsopção

Aqui está um trecho da minha lista de arquivos:

1px.png
misc/feed.png
modules/file/icons/x-office-presentation.png
modules/file/icons/x-office-spreadsheet.png
newsletter.png
sites/all/libraries/ckeditor/plugins/smiley/images/devil_smile.png
sites/all/libraries/ckeditor/plugins/smiley/images/regular_smile.png
sites/default/files/009313_PwC_banner_CBS_Observer_180x246px.jpg

Portanto, uma linha de exemplo seria, com temp sendo o destino:

cp -l --parents 'misc/feed.png' temp

Isso criará esta estrutura:

temp
  misc
    feed.png

Observe que o destino deve estar no mesmo sistema de arquivos que a fonte para os hardlinks funcionarem.

O próximo passo é construir o script:

sed -e "s,^,cp -l --parents '," -e "s,$,' /some/where/temp," filelist > newfilelist

Agora, presumindo que você já tenha criado o diretório vazio / some / where / temp, é possível copiar os arquivos assim:

sh newfilelist 2> missing_files

Observe como os erros terminam em missing_files. O bônus adicional dessa abordagem é que você obterá uma lista de arquivos da lista original que realmente não existem!

Após a execução do script, o temp conterá apenas os arquivos que estão na lista de arquivos, mas sem excluir nada e sem ocupar espaço adicional. Se você estiver satisfeito com o resultado, poderá excluir todos os arquivos originais, incluindo as subpastas.

Por fim, mova os arquivos e pastas de temp novamente para o local original.

Para os 18.000 arquivos, foram necessários apenas alguns segundos.

marlar
fonte
0

Seguro, simples.

cd para o diretório.

Crie um diretório temporário.

mv *.yourExlusionSelector.* ./temp
rm *
mv ./temp ./
rm -rf ./temp

feito.

paradisaeidae
fonte
Bem vindo ao site. Embora sua abordagem funcione se os nomes na lista mencionada pelo OP forem o resultado de uma correspondência simples de padrões - o que pode muito bem ser o caso -, observe que o OP afirmou que os nomes de arquivos a serem excluídos são armazenados em um arquivo específico; convém expandir sua resposta para ler os padrões de exclusão desse arquivo, em vez de confiar em um padrão estático ou ter que copiar potencialmente vários padrões para o console.
AdminBee 28/02