Заметки · 01.08.2025

Нарекаю тебя Копией

После грандиозной работы над ошибками (тык), начинаем отвечать на злободневный вопрос — что делать, если лог FreeFileSync вернул кучу файлов с непомерно массивными именами файлов?

Снова обратимся к магии PowerShell и попробуем отделить путь первичного файла от пути зеркалирования, отбросив всё не очень нужное.

После работы прошлого скрипта у нас имеется файл errors.txt. Вот из него мы и попытаемся добыть все пути к проблемным файлам.

Для начала код скрипта:

# Задаем путь к файлу с ошибками из лога
$logFile = "errors.txt"

# Задаём путь к выходному файлу
$outfile = "out.txt"

# Читаем содержимое файла и ищем строки, содержащие текст в кавычках
$files = Select-String -Path $logFile -Pattern '"(.*?)"' -Encoding default

# Извлекаем только текст между кавычками
$results = $files | ForEach-Object { if ($_ -match '"(D:\\.*?)"') { $matches[1] } }

# Выводим результаты
$results | ForEach-Object { Write-Output $_ } | Out-File $outfile -Force

Обратите внимание на строку:

$results = $files | ForEach-Object { if ($_ -match '"(D:\\.*?)"') { $matches[1] } }

Здесь происходит самое важное: цикл пробегает по переменной $files, которая в свою очередь содержит результат работы командлета Select-String, выбораживает оттуда все пути к проблемным файлам, чей адрес начинается с целевого диска зеркалирования (у меня это диск D) и всё, что нашлось отправляется прямиком в переменную $results.

В своих реалиях переделайте строку на собственный вкус. Главное, чтобы шаблон с упоминанием диска был корректный. В итоге, всё, что удалось накопать скрипту попадает в файл out.txt.

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

Теперь можно смело сказать, что чудо свершилось и мы получили выстраданный список файлов, которые ни в какую не хотят зеркалится на нашу smb-шару. Попробуем их переименовать.

Важный момент номер два: результатом работы скрипта, приведённого ниже, будет массовое переименование общих файлов из основного хранилища. Думаю, стоит напомнить, что данная операция негативно отразится на ярлыках, которые ссылаются на упомянутые файлы, а также на пользовательский опыт нейминга. Очень важно понимать, какие будут последствия, и осознавать всю ответственность за свои дальнейшие действия. Если что — я Вас предупреждал и призывал к анализу кода. Удачи!

Собственно, код скрипта для массового переименования файлов:

# Укажите путь к файлу со списком путей
$listOfPaths = Get-Content -Path "out.txt"

# Максимальная длина имени файла
$maxLength = 100

foreach ($filePath in $listOfPaths) 
{
    # Получаем имя файла и директорию
      # Получаем имя файла без расширения
    $fileName = [System.IO.Path]::GetFileNameWithoutExtension($filePath)
      # Получаем расширение файла
    $fileExtension = [System.IO.Path]::GetExtension($filePath)
    $directory = [System.IO.Path]::GetDirectoryName($filePath)

    # Удаляем точки из имени файла
    $newFileName = $fileName -replace '\.', ''

    # Проверяем длину нового имени файла
    if ($newFileName.Length -gt $maxLength) 
    {
        # Если имя файла превышает максимальную длину - сокращаем имя файла
        $newFileName = $newFileName.Substring(0, $maxLength - 4)
    }

    # Добавляем расширение к новому имени
    $newFileName += $fileExtension

    # Формируем новый путь
    $newFilePath = Join-Path -Path $directory -ChildPath $newFileName

    # Переименовываем файл
    Rename-Item -Path $filePath -NewName $newFilePath

    # Выводим сообщение
    Write-Output "Переименован: $filePath -> $newFilePath"
}

# Ждём действия пользователя
Read-Host

Здесь я не добавлял никаких обработчиков ошибок и проверки наличии файла. Подразумевается, что упоминаемый файл лежит там, где указано в файле журнала программы FreeFileSync.

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

Количество символов в длине имени я ограничил сотней. Можете причесать переменную $maxLength по своему вкусу и под свои потребности.