¿Hay alguna forma de eliminar archivos de una carpeta que están en otra carpeta?

21

Digamos que copio y pego archivos de la carpeta A, que incluye:

Carpeta A:

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

en la carpeta B, que después de actualizar tiene:

Carpeta B:

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

¿Hay alguna forma de eliminar todos los archivos de la carpeta A que están en la carpeta B (marcados con un *)? Además de seleccionar manualmente cada uno y eliminarlo, o ctrl-Z'ing justo después de copiar y pegar

Preferiría un método de Windows o algún software que pudiera hacer esto

¡Gracias!

DarkFire13
fuente
44
¿Cómo sabes que son los mismos archivos en cuanto al contenido? No puedo imaginar un escenario en el que desee considerar ciegamente un archivo como un duplicado solo en función del nombre del archivo.
rory.ap
@roryap Creo que esta pregunta surgió porque OP copió los archivos de la carpeta 1 a la carpeta 2, reemplazó todo y ahora piensa, hmm, esto fue un error, pero se da cuenta de que al día siguiente, así que deshacer no es posible. Pero tienes razón, contentwize no puedes saber.
LPChip
13
Solo una pregunta tonta ... ¿Por qué no usar "cortar" y "pegar"?
DaMachk
@DaMachk si está trabajando con unidades de red o medios extraíbles, copiar-> verificar-> limpieza es una ruta razonable. Si algún proceso utiliza los archivos, puede ser una buena idea probarlo en una copia (lo hago con archivos para el análisis de datos de Python en caso de errores en mi propio código que bloquea el archivo de entrada (por ejemplo). no será tan necesario como solía ser, sino viejos hábitos y todo eso. Alternativamente, el OP podría haber hecho un clic incorrecto en lugar de cortar,
Chris H

Respuestas:

35

Hay un software gratuito llamado WinMerge . Puede usar este software para hacer coincidir duplicados. Primero, use FileOpen, y elija ambos directorios, con la carpeta con los archivos que desea mantener a la izquierda y los que no tiene a la derecha. A continuación, ir a View, y anule la selección Show Different Items, Show Left Unique Itemsy Show Right Unique Items. Esto dejará solo los archivos idénticos que quedan en la lista. Después de eso, elija EditSelect All, haga clic derecho en cualquier archivo y haga clic en DeleteRight. Esto eliminará los duplicados de la carpeta de la derecha.

demo de WinMerge

phyrfox
fuente
El beneficio de este método es que puede detectar si los archivos no son similares en cuanto al contenido, si esto es importante. WinMerge puede comparar todos los factores que son importantes para uno.
25

Esto se puede hacer a través de la línea de comando usando el comando forfiles

Supongamos que tiene la carpeta A ubicada c:\temp\Folder Ay la carpeta B ubicada enc:\temp\Folder B

El comando sería entonces:

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

Una vez hecho esto, la carpeta B tendrá todos los archivos eliminados que están presentes en la carpeta A. Tenga en cuenta que si la carpeta B tiene archivos con el mismo nombre, pero no el mismo contenido, se eliminarán.

Es posible extender esto para trabajar también con carpetas en subcarpetas, pero por temor a que esto se vuelva innecesariamente complicado, he decidido no publicarlo. Requeriría las opciones / sy @relpath (y pruebas adicionales xD)

LPChip
fuente
11

Puede usar este script de 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}
}

Esperemos que sea bastante claro. Analiza cada elemento de la carpeta B, comprueba si hay un elemento con el mismo nombre en la carpeta A y, de ser así, elimina el elemento de la carpeta A. Tenga en cuenta que la ruta final \en la carpeta es importante.

Versión de una línea:

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

Si no le importa si obtiene un diluvio de errores rojos en la consola, puede eliminar el -EA 'SilentlyContinue'.

Guárdelo como un .ps1archivo, por ejemplo dedupe.ps1. Antes de poder ejecutar scripts de PowerShell, deberá habilitar su ejecución:

Set-ExecutionPolicy Unrestricted -Scope CurrentUser

Entonces podrás invocarlo .\dedupe.ps1cuando estés en la carpeta que lo contiene.

Ben N
fuente
4

rsync

rsynces un programa usado para sincronizar el directorio. De las muchas (realmente muchas) opciones que tiene, se explican por sí mismas --ignore-non-existing, --remove-source-filesy --recursive.

Tu puedes hacer

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

si suponemos que tiene los archivos en el directorio A (4) y 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
fuente
4

La respuesta de LPChip es la mejor.

Pero debido a que comencé a aprender Python, pensé: "Diablos, ¿por qué no escribir un script de Python como respuesta a esta pregunta?"

Instalar Python y Send2Trash

Debería instalar Python antes de poder ejecutar el script desde la línea de comandos.

Luego instale Send2Trash para que los archivos eliminados no desaparezcan irremediablemente, sino que terminen en la papelera del sistema operativo:

pip install Send2Trash

Crear guión

Cree un nuevo archivo con, por ejemplo, el nombre DeleteDuplicateInFolderA.py

Copie el siguiente script en el archivo.

#!/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 ejecución en seco, que muestra qué archivos se eliminarían sin eliminar realmente ningún archivo:

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

Modo de eliminación de archivos, que de hecho elimina archivos, así que tenga cuidado:

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

Salida del modo de funcionamiento en 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

Salida del modo de eliminación de archivos

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

Prueba de unidad

Si desea probar la aplicación anterior, cree un archivo llamado DeleteDuplicateInFolderATest.pyy pegue estas pruebas unitarias en ella:

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
fuente
¿Me puede decir por qué este guión es "feo como el infierno"? Acabo de leerlo y lo que estás haciendo es muy claro. Estoy casi tentado a pegarlo en CodeReview.SE para aprender sobre lo que no se prefiere.
usuario1717828
Agregar una suma md5 para verificar si el contenido de los archivos es el mismo sería una buena opción. También usando el mecanismo de basura del sistema operativo en lugar de eliminar
lolesque
@ user1717828: Reestructuré el código, eliminé ese comentario y tomé su sugerencia para publicar el código en CodeReview.SE .
Lernkurve
@lolesque: parte Send2Trash: hecho. ¡Gracias por la idea!
Lernkurve
1
@barlop, estaba respondiendo a la publicación original, no un comentario.
user1717828
1

Usando bash

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

Seguro que podría estar más seguro verificando si el archivo está allí o verificando si el nombre de archivo es seguro. Pero suponiendo que solo quiera hacer esto, y no tenga ningún archivo ridículamente nombrado folderB, esta es una forma rápida y sucia de hacerlo. (y puede usar el emulador de bash que viene con git , si no está ejecutando Win10 + bash)

rm-vanda
fuente
Tal vez necesite agregar un cheque si encuentra directorios ...
Hastur
1

Cualquier programa de estilo NC, como Total Commander, tiene un comando de diferencia de directorio que selecciona archivos en ambas pestañas que son diferentes de la otra pestaña. Llame a este comando, tabal directorio más grande (B), invierta la selección usando *y elimine. Esto tiene la ventaja de no eliminar archivos que pueden haber cambiado (de alguna manera) y no son los mismos aunque estén de acuerdo en su nombre. Puede usar el mismo comando de directorio diff para localizarlos después de la eliminación.

Supongo que estoy atrapado en los noventa ... pero realmente no he visto nada más elegante desde :-) Hasta ahora, esta es la única respuesta que requiere tan solo unas 5 pulsaciones de teclas y ninguna secuencia de comandos / línea de comandos.

El vee
fuente
1

Digamos que copio y pego archivos de la carpeta A en la carpeta B.

¿Hay alguna forma de eliminar todos los archivos de la carpeta A que están en la carpeta B? Además de seleccionar manualmente cada uno y eliminarlo, o ctrl-Z'ing justo después de copiar y pegar

Método de Windows

Si siempre necesita copiar archivos de una ubicación a otra y luego asegurarse de que los archivos que se copiaron correctamente también se eliminen de la ubicación de origen original, a continuación encontrará una solución de secuencia de comandos por lotes que puede usar para automatizar toda esa tarea con solo un Simplemente haga clic en cada ejecución.

  • Asegúrese de ajustar los SourceDiry las DestDirvariables necesarias para sus necesidades.

  • Además, en la parte de la secuencia de comandos a continuación, ("%SourceDir%\*.*") DOsimplemente puede cambiar el *.*valor para que sea más explícito para los nombres de archivo ( File A.txt) o las extensiones de archivo ( *.wav) según sea necesario.


@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 adicionales

Pimp Juice IT
fuente