Есть ли способ удалить файлы из папки, которые находятся в другой папке?

21

Допустим, я копирую и вставляю файлы из папки A, которая включает в себя:

Папка А:

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

в папку B, которая после обновления имеет:

Папка Б:

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

Есть ли способ удалить все файлы из папки A, которые находятся в папке B (помечены *)? Помимо ручного выбора каждого из них и его удаления, или ctrl-Z'ing сразу после копирования-вставки

Я бы предпочел либо метод Windows или какое-то программное обеспечение, которое могло бы сделать это

Благодарность!

DarkFire13
источник
4
Откуда вы знаете, что они одинаковые файлы по содержанию? Я не могу представить сценарий, в котором вы хотели бы слепо рассматривать файл как дубликат, основанный только на одном имени файла.
rory.ap
@roryap Я думаю, что этот вопрос возник, потому что OP скопировал файлы из папки 1 в папку 2, заменил все и теперь думает, хм, это было ошибкой, но понимает, что на следующий день отменить это невозможно. Но ты прав, довольствуйся тем, что не можешь знать.
LPChip
13
Просто тупой вопрос ... Почему бы не использовать «вырезать» и «вставить»?
DaMachk
@DaMachk, если вы работаете с сетевыми дисками или съемными носителями, копирование-> проверка-> очистка - разумный путь. Если файлы используются каким-либо процессом, было бы неплохо проверить его на копии (я делаю это с файлами для анализа данных на python в случае ошибок в моем собственном коде, приводящих к засорению входного файла (например). не так необходимо, как раньше, но старые привычки и все такое. В качестве альтернативы ОП мог бы иметь неправильно щелкнувшую копию вместо вырезанной,
Крис Х

Ответы:

35

Там есть свободное программное обеспечение под названием WinMerge . Вы можете использовать это программное обеспечение для сопоставления дубликатов. Сначала используйте FileOpenи выберите обе директории, с папкой, в которой файлы, которые вы хотите сохранить, слева, и папками, которые вы не сохраняете справа. Затем перейдите Viewи снимите флажок Show Different Items, Show Left Unique Itemsи Show Right Unique Items. Это оставит только идентичные файлы, оставленные в списке. После этого выберите EditSelect All, щелкните правой кнопкой мыши по любому файлу и нажмите DeleteRight. Это удалит дубликаты из правой папки.

демо WinMerge

phyrfox
источник
Преимущество этого метода заключается в том, что он может определять, не являются ли файлы похожими по содержанию, если это важно. WinMerge может сравнить все факторы, которые важны для одного.
25

Это можно сделать через командную строку с помощью команды forfiles

Предположим, что у вас есть папка A c:\temp\Folder A, а папка B - вc:\temp\Folder B

Команда будет тогда:

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

После того, как это будет сделано, в папке B будут удалены все файлы, которые присутствуют в папке A. Имейте в виду, что если в папке B есть файлы с тем же именем, но не одинаковым содержимым, они все равно будут удалены.

Можно также расширить это для работы с папками в подпапках, но из-за страха, что это станет ненужным, я решил не публиковать его. Для этого потребуются параметры / s и @relpath (и дальнейшее тестирование xD)

LPChip
источник
11

Вы можете использовать этот скрипт 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}
}

Надеюсь, это довольно очевидно. Он просматривает каждый элемент в папке B, проверяет, есть ли элемент с таким же именем в папке A, и, если это так, удаляет элемент папки A. Обратите внимание, что финал \в путях к папкам важен.

Однострочная версия:

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

Если вас не волнует, получаете ли вы красные ошибки в консоли, вы можете удалить -EA 'SilentlyContinue'.

Сохраните его как .ps1файл, например dedupe.ps1. Прежде чем вы сможете запускать скрипты PowerShell, вам необходимо включить их выполнение:

Set-ExecutionPolicy Unrestricted -Scope CurrentUser

Тогда вы сможете вызывать его, .\dedupe.ps1находясь в папке, в которой он находится.

Бен Н
источник
4

rsync

rsyncэто программа, используемая для синхронизации каталога Из многих (действительно многих) вариантов, которые у вас есть, есть объяснение --ignore-non-existing, --remove-source-filesи --recursive.

Ты можешь сделать

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

если мы предположим, что у вас есть файлы в каталогах A (4) и 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
источник
4

Ответ LPChip - лучший.

Но поскольку я начал изучать Python, я подумал: «Черт, почему бы не написать скрипт Python в качестве ответа на этот вопрос?»

Установите Python и Send2Trash

Вам нужно будет установить Python, прежде чем вы сможете запустить скрипт из командной строки.

Затем установите Send2Trash, чтобы удаленные файлы не исчезали безвозвратно, а попадали в корзину ОС:

pip install Send2Trash

Создать скрипт

Создайте новый файл с именем, например DeleteDuplicateInFolderA.py

Скопируйте следующий скрипт в файл.

#!/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()

использование

Режим пробного запуска, который показывает, какие файлы будут удалены без фактического удаления файлов:

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

Режим удаления файлов, который действительно удаляет файлы, поэтому будьте осторожны:

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

Выход из режима пробного запуска

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

Вывод режима удаления файла

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

Модульный тест

Если вы хотите протестировать приложение выше, создайте файл с именем DeleteDuplicateInFolderATest.pyи вставьте в него следующие юнит-тесты:

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
источник
Можете ли вы сказать мне, почему этот сценарий "ужасен как ад"? Я просто прочитал это, и то, что вы делаете, кристально ясно. Я почти испытываю желание вставить его в CodeReview.SE, чтобы узнать о том, что не является предпочтительным.
user1717828
Добавление md5sum для проверки того, что содержимое файлов одинаково, было бы хорошим вариантом. Также используя механизм мусора ОС вместо удаления.
lolesque
@ user1717828: Я реструктурировал код, удалил этот комментарий и принял ваше предложение опубликовать код на CodeReview.SE .
Lernkurve
@lolesque: часть Send2Trash: сделано. Спасибо за идею!
Lernkurve
1
@ barlop, я отвечал на оригинальный пост, а не на комментарий.
user1717828
1

Использование Bash

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

Конечно, вы можете быть более безопасным, проверив, существует ли файл, или проверив, является ли имя файла безопасным. Но если предположить, что вы просто хотите это сделать, и у вас нет смехотворно названных файлов folderB- это быстрый и грязный способ сделать это. (и вы можете использовать эмулятор bash, который поставляется с git , если вы не используете Win10 + bash)

гт-Ванда
источник
Может быть, вам нужно добавить проверку, если вы найдете каталоги ...
Хастур
1

Любая программа в стиле NC, такая как Total Commander, имеет команду разницы каталогов, которая выбирает файлы на обеих вкладках, которые отличаются от других вкладок. Вызовите эту команду, tabчтобы увеличить каталог (B), инвертировать выделение с помощью *и удалить. Преимущество этого состоит в том, что вы не удаляете файлы, которые могли (как-то) измениться и не совпадают, хотя и совпадают по названию. Вы можете использовать ту же команду diff каталога, чтобы найти их после удаления.

Я думаю, что застрял в девяностых ... но я не видел ничего более элегантного с тех пор :-) Пока что это единственный ответ, который требует всего лишь 5 нажатий клавиш и никаких скриптов / командной строки вообще.

Ви
источник
1

Допустим, я копирую и вставляю файлы из папки A в папку B.

Есть ли способ удалить все файлы из папки A, которые находятся в папке B? Помимо ручного выбора каждого из них и его удаления, или ctrl-Z'ing сразу после копирования-вставки

Метод Windows

Если вам всегда нужно копировать файлы из одного места в другое, а затем после этого убедиться, что файлы, которые были успешно скопированы, также будут удалены из исходного исходного местоположения, то ниже приведено решение для пакетного сценария, которое можно использовать для автоматизации всей этой задачи с помощью всего лишь просто нажмите каждый прогон.

  • Обязательно установите SourceDirи те DestDirпеременные , соответственно , для ваших нужд.

  • Кроме того, в приведенной ниже части скрипта ("%SourceDir%\*.*") DOвы можете просто изменить *.*значение, чтобы оно было более явным для имен файлов ( File A.txt) или расширений файлов ( *.wav) по мере необходимости.


@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

Дополнительные ресурсы

Сок Pimp IT
источник