Existe uma maneira de remover arquivos de uma pasta que está em outra pasta?

21

Digamos que eu copie e cole arquivos da pasta A, que inclui:

Pasta A:

file1.cfg  
file2.txt  
file3.esp  
file4.bsa  

na pasta B, que após a atualização, possui:

Pasta B:

apples.mp3  
file1.cfg    *
file2.txt    *
file3.esp    *
file4.bsa    *
turtles.jpg

Existe uma maneira de excluir todos os arquivos da pasta A que estão na pasta B (marcados com um *)? Além de selecionar manualmente cada um e excluí-lo, ou pressionar Ctrl-Z'ing logo após a copiar e colar

Eu preferiria um método do Windows ou algum software que pudesse fazer isso

obrigado!

DarkFire13
fonte
4
Como você sabe que eles são os mesmos arquivos em termos de conteúdo? Não consigo imaginar um cenário em que você queira considerar cegamente que um arquivo é uma duplicata apenas com base no nome do arquivo.
22416
@roryap Eu acho que essa questão foi levantada porque o OP copiou os arquivos da pasta 1 para a pasta 2, substituiu tudo e agora pensa, hmm, isso foi um erro, mas percebe que no dia seguinte, então desfazer não é possível. Mas você está certo, com conteúdo que não pode saber.
LPChip
13
Apenas uma pergunta idiota ... Por que não usar "recortar" e "colar"?
DaMachk
@DaMachk se você estiver trabalhando com unidades de rede ou mídia removível, copiar-> verificar-> limpeza é uma rota razoável. Se os arquivos forem usados ​​por algum processo, pode ser uma boa ideia testá-lo em uma cópia (eu faço isso com arquivos para análise de dados python no caso de bugs no meu próprio código que atrapalham o arquivo de entrada (por exemplo). não ser tão necessário como costumava ser, mas velhos hábitos e tudo o que Alternativamente, o OP poderia ter mis-clicado cópia em vez de corte,.
Chris H

Respostas:

35

Existe um software livre chamado WinMerge . Você pode usar este software para combinar duplicatas. Primeiro, use FileOpene escolha os dois diretórios, com a pasta com os arquivos que você deseja manter à esquerda e os que não deseja à direita. Então, vá para Viewe desmarque Show Different Items, Show Left Unique Itemse Show Right Unique Items. Isso deixará apenas os arquivos idênticos restantes na lista. Depois disso, escolha EditSelect All, clique com o botão direito do mouse em qualquer arquivo e clique em DeleteRight. Isso excluirá as duplicatas da pasta da direita.

demonstração do WinMerge

phyrfox
fonte
O benefício desse método é que ele pode detectar se os arquivos não são semelhantes ao conteúdo, se isso é importante. O WinMerge pode comparar todos os fatores importantes para um.
25

Isso pode ser feito através da linha de comando usando o comando forfiles

Vamos supor que você tenha a Pasta A localizada c:\temp\Folder Ae a Pasta B localizada emc:\temp\Folder B

O comando seria então:

c:\>forfiles /p "c:\temp\Folder A" /c "cmd /c del c:\temp\Folder B\@file"

Depois disso, a Pasta B removerá todos os arquivos presentes na Pasta A. Lembre-se de que, se a pasta B tiver arquivos com o mesmo nome, mas não com o mesmo conteúdo, eles ainda serão excluídos.

É possível estender isso para trabalhar com pastas em subpastas também, mas, com medo de que isso se torne desnecessário e complicado, decidi não publicá-lo. Exigiria as opções / se @relpath (e mais testes xD)

LPChip
fonte
11

Você pode usar este script do PowerShell:

$folderA = 'C:\Users\Ben\test\a\' # Folder to remove cross-folder duplicates from
$folderB = 'C:\Users\Ben\test\b\' # Folder to keep the last remaining copies in
Get-ChildItem $folderB | ForEach-Object {
    $pathInA = $folderA + $_.Name
    If (Test-Path $pathInA) {Remove-Item $pathInA}
}

Espero que seja bastante auto-explicativo. Ele analisa todos os itens da Pasta B, verifica se há um item com o mesmo nome na Pasta A e, se houver, remove o item da Pasta A. Observe que a final \nos caminhos da pasta é importante.

Versão de uma linha:

gci 'C:\Users\Ben\test\b\' | % {del ('C:\Users\Ben\test\a\' + $_.Name) -EA 'SilentlyContinue'}

Se você não se importa se recebe um dilúvio de erros vermelhos no console, pode remover o -EA 'SilentlyContinue'.

Salve-o como um .ps1arquivo, por exemplo dedupe.ps1. Antes de executar os scripts do PowerShell, você precisará habilitar a execução deles:

Set-ExecutionPolicy Unrestricted -Scope CurrentUser

Você poderá invocá-lo .\dedupe.ps1quando estiver na pasta que o contém.

Ben N
fonte
4

rsync

rsyncé um programa usado para sincronizar o diretório. Dos muitos (realmente muitos) opções que você tem, há o auto-explicativa --ignore-non-existing, --remove-source-filese --recursive.

Você pode fazer

rsync -avr --ignore-non-existing --recursive --remove-source-files   B/ A -v

se supusermos que você tenha os arquivos nos diretórios A (4) e B (4 + 2).

A       B
├── a   ├── a
├── b   ├── b
├── c   ├── c
└── d   ├── d
        ├── e
        └── f     # Before


A       B
├── a   ├── e
├── b   └── f
├── c   
└── d             # After
Hastur
fonte
4

A resposta da LPChip é a melhor.

Mas como comecei a aprender Python, pensei: "Caramba, por que não escrever um script Python como resposta a esta pergunta?"

Instale o Python e o Send2Trash

Você precisaria instalar o Python antes de poder executar o script na linha de comando.

Em seguida, instale o Send2Trash para que os arquivos excluídos não sejam irrecuperáveis, mas acabem no lixo do sistema operacional:

pip install Send2Trash

Criar script

Crie um novo arquivo com, por exemplo, o nome DeleteDuplicateInFolderA.py

Copie o seguinte script no arquivo

#!/usr/bin/python

import sys
import os
from send2trash import send2trash


class DeleteDuplicateInFolderA(object):
    """Given two paths A and B, the application determines which files are in
       path A which are also in path B and then deletes the duplicates from
       path A.

       If the "dry run" flag is set to 'true', files are deleted. Otherwise
       they are only displayed but not deleted.
    """

    def __init__(self, path_A, path_B, is_dry_run=True):
        self._path_A = path_A
        self._path_B = path_B
        self._is_dry_run = is_dry_run

    def get_filenames_in_folder(self, folder_path):
        only_files = []
        for (dirpath, dirnames, filenames) in os.walk(folder_path):
            only_files.extend(filenames)
        return only_files

    def print_files(sel, heading, files):
        print(heading)
        if len(files) == 0:
            print("   none")
        else:
            for file in files:
                print("   {}".format(file))

    def delete_duplicates_in_folder_A(self):
        only_files_A = self.get_filenames_in_folder(self._path_A)
        only_files_B = self.get_filenames_in_folder(self._path_B)

        files_of_A_that_are_in_B = [file for file in only_files_A if file in only_files_B]

        self.print_files("Files in {}".format(self._path_A), only_files_A)
        self.print_files("Files in {}".format(self._path_B), only_files_B)

        if self._is_dry_run:
            self.print_files("These files would be deleted: ", [os.path.join(self._path_A, file) for file in files_of_A_that_are_in_B])
        else:
            print("Deleting files:")
            for filepath in [os.path.join(self._path_A, file) for file in files_of_A_that_are_in_B]:
                print("   {}".format(filepath))
                # os.remove(filepath)  # Use this line instead of the next if Send2Trash is not installed
                send2trash(filepath)

if __name__ == "__main__":
    if len(sys.argv) == 4:
        is_dry_run_argument = sys.argv[3]
        if not is_dry_run_argument == "--dryrun":
            println("The 3rd argument must be '--dryrun' or nothing.")
        else:
            app = DeleteDuplicateInFolderA(sys.argv[1], sys.argv[2], is_dry_run=True)
    else:
        app = DeleteDuplicateInFolderA(sys.argv[1], sys.argv[2], is_dry_run=False)
    app.delete_duplicates_in_folder_A()

Uso

Modo de execução a seco, que mostra quais arquivos seriam excluídos sem excluir nenhum arquivo:

c:\temp> python .\DeleteDuplicateInFolderA.py c:\temp\test\A c:\temp\test\B --dryrun

Modo de exclusão de arquivo, que de fato exclui arquivos, tenha cuidado:

c:\temp> python .\DeleteDuplicateInFolderA.py c:\temp\test\A c:\temp\test\B

Saída do modo de funcionamento a seco

Files in C:\temp\A
  1.txt
  2.txt
Files in C:\temp\B
  2.txt
  3.txt
These files would be deleted:
  C:\temp\A\2.txt

Saída do modo de exclusão de arquivo

Files in C:\temp\A
  1.txt
  2.txt
Files in C:\temp\B
  2.txt
  3.txt
Deleting files:
  C:\temp\A\2.txt

Teste de unidade

Se você quiser testar o aplicativo acima, crie um arquivo chamado DeleteDuplicateInFolderATest.pye cole esses unittests nele:

import unittest
import os
import shutil
from DeleteDuplicateInFolderA import DeleteDuplicateInFolderA


class DeleteDuplicateInFolderATest(unittest.TestCase):

    def __init__(self, *args, **kwargs):
        super(DeleteDuplicateInFolderATest, self).__init__(*args, **kwargs)
        self._base_directory = r"c:\temp\test"
        self._path_A = self._base_directory + r"\A"
        self._path_B = self._base_directory + r"\B"

    def create_folder_and_create_some_files(self, path, filename_list):
        if os.path.exists(path):
            shutil.rmtree(path)
        os.makedirs(path)
        for filename in filename_list:
            open(os.path.join(path, filename), "w+").close()

    def setUp(self):
        # Create folders and files for testing
        self.create_folder_and_create_some_files(self._path_A, ["1.txt", "2.txt"])
        self.create_folder_and_create_some_files(self._path_B, ["2.txt", "3.txt"])

    def tearDown(self):
        for path in [self._path_A, self._path_B, self._base_directory]:
            if os.path.exists(path):
                shutil.rmtree(path)

    def test_duplicate_file_gets_deleted(self):
        # Arrange
        app = DeleteDuplicateInFolderA(self._path_A, self._path_B, is_dry_run=False)

        # Act
        app.delete_duplicates_in_folder_A()

        # Assert
        self.assertFalse(os.path.isfile(self._path_A + r"\2.txt"), "File 2.txt has not been deleted.")

    def test_duplicate_file_gets_not_deleted_in_mode_dryrun(self):
        # Arrange
        app = DeleteDuplicateInFolderA(self._path_A, self._path_B, is_dry_run=True)

        # Act
        app.delete_duplicates_in_folder_A()

        # Assert
        self.assertTrue(os.path.isfile(self._path_A + r"\2.txt"), "File 2.txt should not have been deleted in mode '--dryrun'")

def main():
    unittest.main()

if __name__ == '__main__':
    main()
Lernkurve
fonte
Você pode me dizer por que esse script é "feio como o inferno"? Acabei de ler e o que você está fazendo é claro. Estou quase tentado a colá-lo no CodeReview.SE para aprender sobre o que não é o preferido.
user1717828
Adicionar um md5sum para verificar se o conteúdo dos arquivos é o mesmo seria uma boa opção. Também usando o mecanismo de lixo do SO em vez de removê-lo.
Lollsque
@ user1717828: reestruturei o código, excluí esse comentário e aceitei sua sugestão para publicar o código no CodeReview.SE .
Lernkurve 28/06
@lolesque: parte Send2Trash: concluída. Obrigado pela idéia!
Lernkurve
11
@ Barlop, eu estava respondendo à postagem original, não um comentário.
user1717828
1

Usando o bash

for f in $(ls /path/to/folderB/); do 
    rm -rf /path/to/folderA/$f
done

Claro que você pode estar mais seguro verificando se o arquivo está lá ou se o nome do arquivo é seguro. Mas supondo que você só queira fazer isso e não tenha arquivos com nomes ridículos folderB- essa é uma maneira rápida e suja de fazê-lo. (e você pode usar o emulador de bash que acompanha o git , se você não estiver executando o Win10 + bash)

rm-vanda
fonte
Talvez você precise adicionar uma verificação se encontrar diretórios ...
Hastur
1

Qualquer programa no estilo NC, como o Total Commander, possui um comando de diferença de diretório que seleciona arquivos nas duas guias diferentes da outra guia. Chame este comando, tabpara o diretório maior (B), inverta a seleção usando* e exclua. Isso tem a vantagem de não excluir arquivos que podem ter sido alterados (de alguma forma) e não são os mesmos, embora concordem em nome. Você pode usar o mesmo comando diff de diretório para localizá-los após a exclusão.

Acho que estou preso nos anos noventa ... mas não vejo nada mais elegante desde :-) Até agora, esta é a única resposta que requer apenas cinco toques de tecla e nenhuma linha de script / comando.

The Vee
fonte
1

Digamos que eu copie e cole arquivos da pasta A na pasta B.

Existe uma maneira de excluir todos os arquivos da pasta A que estão na pasta B? Além de selecionar manualmente cada um e excluí-lo, ou pressionar Ctrl-Z'ing logo após a copiar e colar

Método Windows

Se você sempre precisar copiar arquivos de um local para outro e depois garantir que os arquivos que foram copiados com êxito também sejam excluídos do local de origem original, a seguir há uma solução de script em lote que você pode usar para automatizar toda a tarefa com apenas um clique simples em cada corrida.

  • Certifique-se de definir SourceDiras DestDirvariáveis ​​e de acordo com suas necessidades.

  • Além disso, na parte do script abaixo, ("%SourceDir%\*.*") DOvocê pode simplesmente alterar o *.*valor para ser mais explícito para nomes de arquivos ( File A.txt) ou extensões de arquivo ( *.wav) conforme necessário.


@ECHO ON
SET SourceDir=C:\Users\User\Desktop\Source
SET DestDir=C:\Users\User\Desktop\Dest

FOR %%A IN ("%SourceDir%\*.*") DO XCOPY /F /Y "%%~A" "%DestDir%\" && DEL /Q /F "%%~A"
GOTO EOF

Recursos adicionais

Pimp Juice IT
fonte