มีวิธีลบไฟล์จากโฟลเดอร์ที่อยู่ในโฟลเดอร์อื่นหรือไม่?


21

สมมติว่าฉันคัดลอกและวางไฟล์จากโฟลเดอร์ A ซึ่งรวมถึง:

โฟลเดอร์ A:

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

ในโฟลเดอร์ B ซึ่งหลังจากอัพเดตมี:

โฟลเดอร์ B:

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

มีวิธีลบไฟล์ทั้งหมดจากโฟลเดอร์ A ที่อยู่ในโฟลเดอร์ B (ทำเครื่องหมายด้วย *) หรือไม่? นอกเหนือจากการเลือกแต่ละรายการด้วยตนเองและลบมันหรือ ctrl-Z'ing ทันทีหลังจากคัดลอกวาง

ฉันต้องการวิธี windows หรือซอฟต์แวร์บางอย่างที่สามารถทำได้

ขอบคุณ!


4
คุณจะรู้ได้อย่างไรว่าพวกเขาเป็นไฟล์เดียวกันกับเนื้อหาที่ฉลาด? ฉันไม่สามารถจินตนาการถึงสถานการณ์ที่คุณต้องการพิจารณาว่าไฟล์เป็นไฟล์ที่ซ้ำซ้อนโดยอ้างอิงจากชื่อไฟล์เพียงอย่างเดียว
rory.ap

@Roryap ฉันคิดว่าคำถามนี้เกิดขึ้นเนื่องจาก OP คัดลอกไฟล์จากโฟลเดอร์ 1 ไปยังโฟลเดอร์ 2 แทนที่ทั้งหมดและตอนนี้คิดว่าอืมนี่เป็นข้อผิดพลาด แต่ตระหนักว่าในวันถัดไปจึงไม่สามารถยกเลิกได้ แต่คุณพูดถูกต้องเอาแต่ใจไม่ได้
LPChip

13
แค่คำถามโง่ ๆ ... ทำไมไม่ใช้ "ตัด" และ "วาง"?
DaMachk

@DaMachk หากคุณกำลังทำงานกับไดรฟ์เครือข่ายหรือสื่อที่ถอดได้คัดลอก -> ตรวจสอบ -> การทำความสะอาดเป็นเส้นทางที่เหมาะสม หากกระบวนการบางไฟล์ใช้อาจเป็นความคิดที่ดีที่จะทดสอบบนสำเนา (ฉันทำสิ่งนี้กับไฟล์สำหรับการวิเคราะห์ข้อมูลไพ ธ อนในกรณีที่มีข้อบกพร่องในรหัสของตัวเองที่ครอบคลุมไฟล์อินพุต (ตัวอย่าง)) ไม่จำเป็นเท่าที่เคยเป็นมา แต่เป็นนิสัยเก่าแก่และทุกอย่างอีกทางหนึ่งหรือ OP อาจมีการคัดลอกที่ผิดพลาดแทนการตัด
Chris H

คำตอบ:


35

มีซอฟต์แวร์ฟรีออกมีที่เรียกว่าเป็นWinMerge คุณสามารถใช้ซอฟต์แวร์นี้เพื่อจับคู่ที่ซ้ำกัน ขั้นแรกให้ใช้FileOpenและเลือกทั้งสองไดเรกทอรีโดยใช้โฟลเดอร์ที่มีไฟล์ที่คุณต้องการเก็บไว้ทางซ้ายและรายการที่คุณไม่ได้อยู่ด้านขวา จากนั้นไปViewและยกเลิกการเลือกShow Different Items, และShow Left Unique Items Show Right Unique Itemsการทำเช่นนี้จะทำให้มีเพียงไฟล์เดียวที่เหลืออยู่ในรายการ หลังจากนั้นเลือกEditSelect Allคลิกขวาบนไฟล์ใด ๆ และคลิกที่→Delete Rightนี่จะเป็นการลบรายการที่ซ้ำกันออกจากโฟลเดอร์ทางขวา

ตัวอย่างของ WinMerge


ประโยชน์ของวิธีนี้คือมันสามารถตรวจจับได้ว่าไฟล์ไม่ได้มีเนื้อหาที่คล้ายกันหรือไม่หากเป็นสิ่งสำคัญ WinMerge สามารถเปรียบเทียบปัจจัยทั้งหมดที่สำคัญกับหนึ่ง

25

สิ่งนี้สามารถทำได้ผ่าน commandline โดยใช้คำสั่ง 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)


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

4

คำตอบของ LPChipนั้นดีกว่า

แต่เนื่องจากฉันเริ่มเรียนรู้ Python ฉันจึงคิดว่า "Heck ทำไมไม่เขียนสคริปต์ 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และวาง unittests เหล่านี้ลงไป:

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()

คุณบอกฉันได้ไหมว่าทำไมสคริปต์นี้ถึงน่าเกลียดเหมือนนรก ฉันเพิ่งอ่านมันและสิ่งที่คุณกำลังทำคือใส ฉันเกือบอยากจะวางมันลงบน CodeReview.SE เพื่อเรียนรู้เกี่ยวกับสิ่งที่ไม่ต้องการเกี่ยวกับมัน
user1717828

การเพิ่ม md5sum เพื่อตรวจสอบว่าเนื้อหาไฟล์เหมือนกันจะเป็นตัวเลือกที่ดีหรือไม่ ยังใช้กลไกถังขยะระบบปฏิบัติการแทนการลบ
lolesque

@ user1717828: ฉันได้ปรับโครงสร้างหนี้รหัสลบความคิดเห็นที่และเอาคำแนะนำของคุณที่จะโพสต์โค้ดบน CodeReview.SE
Lernkurve

@lolesque: ส่วน Send2Trash: เสร็จแล้ว ขอบคุณสำหรับความคิด!
Lernkurve

1
@barlop ฉันตอบกลับโพสต์ดั้งเดิมไม่ใช่ความคิดเห็น
1717828

1

ใช้ทุบตี

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) สลับการเลือกโดยใช้*และลบ นี่เป็นข้อดีของการไม่ลบไฟล์ที่อาจมีการเปลี่ยนแปลง (อย่างใด) และไม่เหมือนกับที่พวกเขาเห็นด้วยในชื่อ คุณสามารถใช้คำสั่งไดเรกทอรีเดียวกันเพื่อค้นหาสิ่งเหล่านี้หลังจากการลบ

ฉันเดาว่าฉันติดอยู่ในยุคเก้าสิบ ... แต่ฉันไม่ได้เห็นอะไรหรูหรามากขึ้นตั้งแต่ :-) จนถึงตอนนี้นี่เป็นคำตอบเดียวที่ต้องใช้การกดแป้น 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

แหล่งข้อมูลเพิ่มเติม

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.