Назад | Перейти на главную страницу

Как лучше всего удалить дубликаты файлов на FTP-сервере веб-хостинга?

По какой-то причине (это случилось до того, как я начал работать над этим проектом) - на веб-сайте моего клиента есть 2 дубликата каждого файла. Фактически утроение размера сайта.

Файлы выглядят примерно так:

wp-comments-post.php    |    3,982 bytes
wp-comments-post (john smith's conflicted copy 2012-01-12).php    |    3,982 bytes
wp-comments-post (JohnSmith's conflicted copy 2012-01-14).php    |    3,982 bytes

Хостинг, на котором находится веб-сайт, не имеет доступа к bash или SSH.

Как вы считаете, как проще всего удалить эти повторяющиеся файлы, что займет меньше всего времени?

Я написал сценарий поиска дубликатов в PowerShell, используя Сборка WinSCP .NET.

Актуальная и улучшенная версия этого скрипта теперь доступна как расширение WinSCP.
Найдите дубликаты файлов на SFTP / FTP сервере.

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

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

powershell.exe -File find_duplicates.ps1 -sessionUrl ftp://user:password@example.com/ -remotePath /path

Сценарий такой:

param (
    # Use Generate URL function to obtain a value for -sessionUrl parameter.
    $sessionUrl = "sftp://user:mypassword;fingerprint=ssh-rsa-xxxxxxxxx...=@example.com/",
    [Parameter(Mandatory)]
    $remotePath,
    $remoteChecksumAlg = $Null
)

function FileChecksum ($remotePath)
{
    if (!($checksums.ContainsKey($remotePath)))
    {
        if ($remoteChecksumAlg -eq $Null)
        {
            Write-Host "Downloading file $remotePath..."
            # Download file
            $localPath = [System.IO.Path]::GetTempFileName()
            $transferResult = $session.GetFiles($remotePath, $localPath)

            if ($transferResult.IsSuccess)
            {
                $stream = [System.IO.File]::OpenRead($localPath)
                $checksum = [BitConverter]::ToString($sha1.ComputeHash($stream))
                $stream.Dispose()

                Write-Host "Downloaded file $remotePath checksum is $checksum"

                Remove-Item $localPath
            }
            else
            {
                Write-Host ("Error downloading file ${remotePath}: " +
                    $transferResult.Failures[0])
                $checksum = $False
            }
        }
        else
        {
            Write-Host "Request checksum for file $remotePath..."
            $buf = $session.CalculateFileChecksum($remoteChecksumAlg, $remotePath)
            $checksum = [BitConverter]::ToString($buf)
            Write-Host "File $remotePath checksum is $checksum"
        }

        $checksums[$remotePath] = $checksum
    }

    return $checksums[$remotePath]
}

function FindDuplicatesInDirectory ($remotePath)
{
    Write-Host "Finding duplicates in directory $remotePath ..."

    try
    {
        $directoryInfo = $session.ListDirectory($remotePath)

        foreach ($fileInfo in $directoryInfo.Files)
        {
            $remoteFilePath = ($remotePath + "/" + $fileInfo.Name) 

            if ($fileInfo.IsDirectory)
            {
                # Skip references to current and parent directories
                if (($fileInfo.Name -ne ".") -and
                    ($fileInfo.Name -ne ".."))
                {
                    # Recurse into subdirectories
                    FindDuplicatesInDirectory $remoteFilePath
                }
            }
            else
            {
                Write-Host ("Found file $($fileInfo.FullName) " +
                    "with size $($fileInfo.Length)")

                if ($sizes.ContainsKey($fileInfo.Length))
                {
                    $checksum = FileChecksum($remoteFilePath)

                    foreach ($otherFilePath in $sizes[$fileInfo.Length])
                    {
                        $otherChecksum = FileChecksum($otherFilePath)

                        if ($checksum -eq $otherChecksum)
                        {
                            Write-Host ("Checksums of files $remoteFilePath and " +
                                "$otherFilePath are identical")
                            $duplicates[$remoteFilePath] = $otherFilePath
                        }
                    }
                }
                else
                {
                    $sizes[$fileInfo.Length] = @()
                }

                $sizes[$fileInfo.Length] += $remoteFilePath
            }
        }
    }
    catch [Exception]
    {
        Write-Host "Error processing directory ${remotePath}: $($_.Exception.Message)"
    }
}

try
{
    # Load WinSCP .NET assembly
    Add-Type -Path "WinSCPnet.dll"

    # Setup session options from URL
    $sessionOptions = New-Object WinSCP.SessionOptions
    $sessionOptions.ParseUrl($sessionUrl)

    $session = New-Object WinSCP.Session
    $session.SessionLogPath = "session.log"

    try
    {
        # Connect
        $session.Open($sessionOptions)

        $sizes = @{}
        $checksums = @{}
        $duplicates = @{}

        $sha1 = [System.Security.Cryptography.SHA1]::Create()

        # Start recursion
        FindDuplicatesInDirectory $remotePath
    }
    finally
    {
        # Disconnect, clean up
        $session.Dispose()
    }

    # Print results
    Write-Host

    if ($duplicates.Count -gt 0)
    {
        Write-Host "Duplicates found:"

        foreach ($path1 in $duplicates.Keys)
        {
            Write-Host "$path1 <=> $($duplicates[$path1])"
        }
    }
    else
    {
        Write-Host "No duplicates found."
    }

    exit 0
}
catch [Exception]
{
    Write-Host "Error: $($_.Exception.Message)"
    exit 1
}

(Я автор WinSCP)

Изменить: используйте ftpfs для монтирования удаленной файловой системы ftp в локальной точке монтирования, затем используйте любой другой подход, описанный здесь.

Если все файлы соответствуют этому синтаксису, вы можете, например,

rbos@chili:~/tmp$ touch asdf.php
rbos@chili:~/tmp$ touch "asdf (blah blah blah).php"
rbos@chili:~/tmp$ touch "asdf (blah blah rawr).php"
rbos@chili:~/tmp$ find | grep "(.*)"
./asdf (blah blah rawr).php
./asdf (blah blah blah).php

чтобы сопоставить файлы, а затем просто передайте это в xargs или цикл для проверки списка:

find | grep "(.*)" | while read i; do echo "$i";done | less

а затем заменить echo с участием rm как только вы убедитесь, что список точен.

Ты можешь использовать FSlint чтобы найти повторяющиеся файлы.

FTP на сервер и rm файлы.

Запустите это: find /yourdir -name "*conflicted copy*" -type f -ls

Если перечисленные файлы - это те, которые вы хотите удалить, измените -ls для -delete и снова запустите.

Я предлагаю сначала сделать резервную копию вашего базового каталога с помощью tar, прежде чем вы это сделаете ...

РЕДАКТИРОВАТЬ: Я только что понял, что у вас нет доступа к сеансу оболочки, поэтому это не сработает для вас ...

Вероятно, вам понадобится что-то вроде этого: http://www.go4expert.com/forums/showthread.php?t=2348 чтобы рекурсивно выгрузить список файлов, а затем создать другой сценарий, который удаляет только те, которые вам нужны.