Как переместить первые 7 символов имени файла в конец с помощью Powershell

У моей компании миллионы старых отчетов в формате PDF. Они обычно называются в формате: 2018-09-18 - ReportName.pdf

Организация, в которую мы должны их отправить, теперь требует, чтобы мы назвали файлы в следующем формате: Report Name - 2018-09.pdf

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

Спасибо!

4 ответа

Решение

Предостережение:

  • Как указывает jazzdelightsme, желаемая операция переименования может привести к конфликтам имен, учитывая, что вы jazzdelightsme компонент дня из ваших дат; например, 2018-09-18 - ReportName.pdf а также 2018-09-19 - ReportName.pdf приведет к тому же имени файла, Report Name - 2018-09.pdf,

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

Get-ChildItem -Filter *.pdf | Rename-Item -NewName { 
  $_.Name -replace '^(\d{4}-\d{2})-\d{2} - (.*?)\.pdf$', '$2 - $1.pdf' 
} -WhatIf

-WhatIf предварительный просмотр операции переименования; удалите его, чтобы выполнить фактическое переименование.
добавлять -Recurse к Get-CildItem вызов для обработки целого поддерева каталога.
Использование -Filter необязательно, но ускоряет обработку.

  • Блок скрипта ({ ... }) передается Rename-Item "s -NewName параметр, который включает динамическое переименование каждого входного файла ($_) полученные от Get-ChildItem используя выражение преобразования строки (замены).

  • -replace оператор использует регулярное выражение (регулярное выражение) в качестве первого операнда для выполнения замены строк на основе шаблонов; здесь регулярное выражение разбивается следующим образом:

    • ^(\d{4}-\d{2}) соответствует что-то вроде 2018-09 в начале (^) имени и - в силу того, что (...) - захваты, которые совпадают в так называемой группе захвата, на которую можно ссылаться в строке замены по ее индексу, а именно $1, потому что это первая группа захвата.

    • (.*?) захватывает остальную часть имени файла, исключая расширение в группе захвата $2,

      • ? после .* делает подвыражение нежадным, что означает, что оно также даст возможность сопоставить последующие подвыражения, в отличие от попытки сопоставить как можно больше символов (это поведение по умолчанию, называемое жадным).
    • \.pdf$ соответствует расширению имени файла (.pdf) в конце ($) - обратите внимание, что дело не имеет значения. . сбежал как \. потому что это должно быть подобрано буквально здесь (без побега, . соответствует любому отдельному символу в одной строке).

    • $2 - $1.pdf является строкой замены, которая упорядочивает записи групп захвата в желаемой форме.

Обратите внимание, что любой файл, имя которого не соответствует регулярному выражению, спокойно остается один, потому что -replace оператор пропускает входную строку, если совпадений нет, и Rename-Item ничего не делает, если новое имя совпадает со старым.

Что-то вроде этого -

Get-ChildItem -path $path | Rename-Item -NewName {$_.BaseName.Split(' - ')[-1] + ' - ' + $_.BaseName.SubString(0,7) + $_.Extension} -WhatIf

Пояснение -

  1. Split выделит имя файла в зависимости от параметра - а также [-1] говорит PowerShell выбрать последнее из сегрегированных значений.
  2. SubString(0,7) выберет 7 символов, начиная с первого символа BaseName файла.
  3. Удалить -WhatIf применить переименование.

Это должно работать (добавлены некоторые тестовые данные):

$test = '2018-09-18 - ReportName.pdf','2018-09-18 - Other name.pdf','other pattern.pdf','2018-09-18 - double.extension.pdf'

$test | % {
    $match = [Regex]::Match($_, '(?<Date>\d{4}-\d\d)-\d\d - (?<Name>.+)\.pdf')
    if ($match.Success) {
        "$($match.Groups['Name'].Value) - $($match.Groups['Date'].Value).pdf"
    } else {
        $_
    }
}

Get-ChildItem с некоторыми RegEx и Rename-Item может сделать это:

Get-ChildItem -Path "C:\temp" | foreach {

    $newName = $_.Name -replace '(^.{7}).*?-\s(.*?)\.(.*$)','$2 - $1.$3'
    $_ | Rename-Item -NewName $newName
}

RegEx

'(^.{7}).*?-\s(.*?)\.(.*$)' / $2 - $1.$3

  • (^.{7}) соответствует первым 7 символам
  • .*?-\s соответствует любым символам до (и включая) первого найденного - (космическая тире)
  • (.*?)\. соответствует чему угодно до первой найденной точки ( .)
  • (.*$) соответствует расширению файла в этом случае
  • $2 - $1.$3 складывает все вместе в измененном порядке

Это не будет правильно работать, если есть имена файлов с несколькими точками ( .).

Другие вопросы по тегам