Как удалить дубликаты файлов с похожим именем - PullRequest
1 голос
/ 28 октября 2019

Я довольно новичок в PowerShell и не смог найти окончательного ответа на мою проблему. У меня есть куча файлов Excel в разных папках, которые являются дубликатами, но имеют разные имена файлов из-за их обновления. например, 015 Утвержденная гарантия - Турция - Корпус-2019 08-1437015 (выпуск 3), 015 Утвержденная гарантия - Турция - Корпус-2019 08-1437015 (финальный выпуск) 015 Утвержденная гарантия - Турция - Корпус-2019 08-1437015 015 Утвержденная гарантия -Турция - дело 2019 08-1437015 с поправками

Я пробовал разные вещи, но теперь я знаю самый простой способ фильтрации файлов, но не знаю синтаксис. Узловая точка будет иметь место номер только после даты. Я хочу сравнить номера дел друг с другом и сохранить только самые новые (по дате изменения) и удалить остальные. Любое руководство приветствуется.

#take files from folder
$dupesource = 'C:\Users\W_Brooker\Documents\Destination\2019\08'

#filter files by case number (7 digit number after date)
$files = Get-ChildItem $dupesource -Filter "08-aaaaaaa"

#If case number is the same keep newest file delete rest
foreach ($file in $files){
$file | Delete-Item - sort -property Datemodified |select -Last 1
}

Ответы [ 3 ]

1 голос
/ 28 октября 2019

Идиоматическое решение PowerShell заключается в:

  • объединении нескольких командлетов в одном конвейере,

  • , в котором Group-Object обеспечиваетОсновные функции группировки дубликатов файлов по общему номеру дела в имени файла:

# Define the regex that matches a case number:
# A 7-digit number embedded in filenames that duplicates share.
$regex = '\b\d{7}\b' 

# Enumerate all files and select only those whose name contains a case number.
Get-ChildItem -File $dupesource | Where-Object { $_.BaseName -match $regex } | 
  # Group the resulting files by shared embedded case number.
  Group-Object -Property { [regex]::Match($_.BaseName, $regex).Value } |
    # Process each group:
    ForEach-Object {
      # In each group, sort files by most recently updated first.
      $_.Group | Sort-Object -Descending LastWriteTimeUtc |
        # Skip the most recent file and delete the older ones.
        Select-Object -Skip 1 | Remove-Item -WhatIf
    }

Общий параметр -WhatIf Предварительный просмотр операция. Удалите его, если вы уверены, что он будет делать то, что вы хотите.

0 голосов
/ 28 октября 2019

Вот альтернатива, использующая командлет PowerShell Group-Object.

Использует регулярное выражение для сопоставления файлов с номером дела, игнорируя те, у которых нет номера дела. См. Снимок экрана внизу, на котором показаны тестовые данные (набор тестовых файлов xlsx)

cls

#Assume that each file has an xlsx extension.
#Assume that a case number always looks like this: "Case-YYYY~XX-Z" where YYYY is 4 digits, ~ is a single space, XX is two digits, and Z is one-to-many-digits

#make a list of xlsx files (recursive)
$files = Get-ChildItem -LiteralPath .\ExcelFiles -Recurse -Include *.xlsx 

#$file is a System.IO.FileInfo object. Parse out the Case number and add it to the $file object as CaseNumber property
foreach ($file in $files)
{
    $Matches = $null

    $file.Name -match "(^.*)(Case-\d{4}\s{1}\d{2}-\d{1,})(.*\.xlsx$)" | out-null

    if ($Matches.Count -eq 4)
    {
        $caseNumber = $Matches[2]
        $file | Add-Member -NotePropertyName CaseNumber -NotePropertyValue $caseNumber
    }
    Else
    {
        #child folders will end up in this group too
        $file | Add-Member -NotePropertyName CaseNumber -NotePropertyValue "NoCaseNumber"
    }
}

#group the files by CaseNumber
$files | Group-Object -Property CaseNumber -OutVariable fileGroups | out-null

foreach ($fileGroup in $fileGroups)
{
    #skip folders and files that don't have a valid case #
    if ($fileGroup.Name -eq "NoCaseNumber")
    {
        continue
    }

    #for each group: sort files descending by LastWriteTime. Newest file will be first, so skip 1st file and remove the rest
    $fileGroup.Group | sort -Descending -Property LastWriteTime | select -skip 1 | foreach {Remove-Item -LiteralPath $_.FullName -Force}
}

Тестовые данные

enter image description here

0 голосов
/ 28 октября 2019

Это должно сработать:

    $files = Get-ChildItem 'C:\Users\W_Brooker\Documents\Destination\2019\08' -Recurse

    # create datatable to store file Information in it

    $dt = New-Object system.Data.DataTable
    [void]$dt.Columns.Add('FileName',[string]::Empty.GetType() )
    [void]$dt.Columns.Add('CaseNumber',[string]::Empty.GetType() )
    [void]$dt.Columns.Add('FileTimeStamp',[DateTime]::MinValue.GetType() )
    [void]$dt.Columns.Add('DeleteFlag',[byte]::MinValue.GetType() )

    # Step 1: Make inventory

    foreach( $file in $files ) {

    if( !$file.PSIsContainer -and $file.Extension -like '.xls*' -and $file.Name -match '^.*\-\d+ *[\(\.].*$' ) {

        $row               = $dt.NewRow()
        $row.FileName      = $file.FullName
        $row.CaseNumber    = $file.Name -replace '^.*\-(\d+) *[\(\.].*$', '$1'
        $row.FileTimeStamp = $file.LastWriteTime
        $row.DeleteFlag    = 0

        [void]$dt.Rows.Add( $row )
    }
}

# Step 2: Mark files to delete

$rows = $dt.Select('', 'CaseNumber, FileTimeStamp DESC')

$caseNumber = ''

foreach( $row in $rows ) {
    if( $row.CaseNumber -ne $caseNumber ) {
        $caseNumber = $row.CaseNumber
        Continue
    }
    $row.DeleteFlag = 1
    [void]$dt.AcceptChanges()
}

# Step 3: Delete files

$rows = $dt.Select('DeleteFlag = 1', 'FileTimeStamp DESC')

foreach( $row in $rows ) {
    $fileName = $row.FileName
    Remove-Item -Path $fileName -Force | Out-Null
}
...