Копирование файлов в определенные каталоги на основе имени файла

Копирование файлов в определенные каталоги на основе имени файла

У меня на самом деле нет реальных точек соприкосновения с Batch или PowerShell, тем не менее, я создал каталог сотрудников, для которого сейчас ищу решение, в котором документы автоматически копируются в вышеупомянутый каталог с помощью скрипта:

  • Все документы, которые необходимо скопировать, в настоящее время находятся в одной папке и должны быть скопированы в соответствующую подпапку каталога.
  • Имена файлов документов структурированы следующим образом, где XX/ XXXX— переменные, играющие роль в назначении:
    • ABCD_123456_1234567_12345_XX.pdf
      ABCD_123456_1234567_12345_XXXX.pdf
  • Целевые каталоги также содержат их, в настоящее время их около пятидесяти.

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

  • Я уже воспользовался функцией поиска, но не нашел ничего, что точно соответствовало бы данному примеру:
    • C:\ABCD_123456_1234567_12345_99\\11/17/19/1991\Directory\99_ABCD\
      C:\ABCD_123456_1234567_12345_9999\\11/17/19/1991\Directory\9999_ABCD\
    • Возможно:
      C:\ABCD_123456_1234567_12345_999\\11/17/19/1991\ directory\999_ABCD\
      C:\ABCD_123456_1234567_12345_99999\\11/17/19/1991\Directory\99999_ABCD\

Я пытался настроить это с помощью $FileName-split, что не сработало, но мне удалось завершить имена файлов, чтобы фактически посчитать время, например, 30:27:

  • C:\ABCD_123456_1234567_12345_99C:\ABCD_123456_1234567_12345_0099
  • Я борюсь с парсингом папок назначения, поскольку все папки существуют и в их именах файлов есть переменные. Дошло до этого:
    $Directory = "C:\Folder\" 
    $FileNames = (Get-Item $Directory).GetFiles()
    
    foreach($FileName in $FileNames) {
      $Destination = "\\10.10.10.10\destinationfolder"
      { Move-Item -Path $FileName -Destination $Destination }
    }
    

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

Это может визуализировать это

решение1

Это будет решением PowerShell:

$Destination = "\\10.10.10.10\destinationfolder"
Get-ChildItem "C:\Folder" | ForEach-Object {
    $Head = ($_.BaseName -split '_')[0]
    $Tail = ($_.BaseName -split '_')[-1]
    $DestinationPath = Join-Path $Destination ("{0}_{1}" -f $Head, $Tail)
    New-Item -Path $DestinationPath -ItemType Directory -ErrorAction SilentlyContinue
    Copy-Item $_.FullName $DestinationPath -Force
}

Обновлять:

После ваших комментариев, я думаю, это больше то, что вы хотите. Однако вы должны быть уверены, что каждая концовка имеет ровно одну соответствующую папку. Например, если есть папка с именем abc_123и другая с именем, def_123она просто возьмет первую, потому что, согласно вашим комментариям, мы принимаем во внимание только концовку

$Destination = "\\10.10.10.10\destinationfolder"
Get-ChildItem "C:\Folder" | ForEach-Object {
    $Tail = ($_.BaseName -split '_')[-1]
    $Filter = "*_{0}" -f $Tail
    $DestDir = Get-ChildItem $Destination -Filter $Filter -Directory | Select-Object -ExpandProperty FullName -First 1
    if ($DestDir) {
        Copy-Item $_.FullName $DestDir -Force
    } else {
        "No Directory was found that matched the Filter {0} in Directory {1}" -f $Filter, $Destination | Write-Host
    }
}

Он делает следующее:

  • Сначала задайте папку назначения в $Destinationпеременной
  • Получить все элементы в папке с помощью Get-ChildItem(если вам нужно сделать это рекурсивно, добавьте переключатель -Recurse, если вам нужны только определенные типы файлов, добавьте -Filter *.txtили любое другое расширение)
  • перебрать все найденные файлы с помощьюForEach-Object
  • разделите базовое имя (это имя файла без расширения) на две части _и получите последнюю часть базового имени и сохраните ее в $Endingпеременной
  • Вычислите имя папки, отформатировав конечную переменную в имя папки с помощью-f
  • Используйте Join-Pathдля соединения пункта назначения и вычисленной папки и сохранения ее в$Destinationpath
  • Создайте папку, если она не существует, с помощью New-Item. Если она уже существует, эта строка не перезаписывает существующую папку
  • Скопируйте элемент в папку назначения с помощьюCopy-Item

решение2

Я не понимаю вашего вопроса, но если вы хотите скопировать и вставить файл, как этот

ABCD_123456_1234567_12345_1.txt copy into 1_ABCD
ABCD_123456_1234567_12345_2.txt copy into 2_ABCD
ABCD_123456_1234567_12345_3.txt copy into 3_ABCD
ABCD_123456_1234567_12345_9999.txt copy into 9999_ABCD

Тогда, возможно, эта команда cmd вам поможет

echo D|for /l %x in (1,1,20) do @xcopy ABCD_123456_1234567_12345_%x.txt ABC_%x\ /Y /Q

вторая команда с тем же выводом:

for /l %x in (1,1,20) do @xcopy ABCD_123456_1234567_12345_%x.txt ABC_%x\ /Y /Q

вы можете изменить 20на9999 выход

Эта команда создаст папку, если ее нет, а затем вставит файл, а если файл существует внутри папки, он будет перезаписан.

ОБНОВЛЯТЬ:

Команда для поиска имени файла без расширения(и добавить результат в filenamewithoutext.txt)

for /f %x in ('dir /b') do @echo %~nx>>filenamewithoutext.txt

Чтение выходного файла и сохранение переменных в новый файл variables.txt

for /f "tokens=5 delims=_" %x in (filenamewithoutext.txt) do @echo %x>>variables.txt

Чтение переменных и копирование файлов в разные папки

for /f %x in (variables.txt) do @xcopy ABCD_123456_1234567_12345_%x.txt ABC_%x\ /Y /Q

вы можете изменить ABC_%xна%x_ABC

решение3

Прочитав ваш вопрос, я думаю, что это должно сработать:

$Source      = 'C:\Folder\'
$Destination = '\\10.10.10.10\destinationfolder\'

Get-ChildItem -Path $Source -FIlter *.pdf -File | ? { ($BaseName = $_.BaseName) -match '_' } | ForEach{
    $Head       = $BaseName.Split('_')[0]
    $Tail       = $BaseName.Split('_')[-1]       ### (Index of -1) -> Last array element
    $SubFolder  = "$Tail`_$Head"                 ### *** Escaped <underscore> required ***
    If ( Test-Path ( $FullPath = Join-Path $Destination $SubFolder ) ) {
        Copy-Item -Literal $_.FullName $FullPath
    } Else {
        '"{0}" not copied because the directory "{1}" does not exist' -f $_.Name , $FullPath | Write-Output
    }
}

ForEach-ОбъектМетод String.Split()Присоединиться-ПутьТест-ПутьКопировать-элемент


Отладка

Я использовал этот вариант кода для устранения неполадок в манипуляции строками, что выявило проблему использования подчеркивания в строках расширения. Помещает все промежуточные переменные вGridViewдля быстрого сканирования:

Get-ChildItem -Path $Source -FIlter *.pdf -File | ? BaseName -match '_' | ForEach{
    $Head       = $_.BaseName.Split('_')[0]
    $Tail       = $_.BaseName.Split('_')[-1]
    $SubFolder  = "$Tail`_$Head"
    $FullPath   = Join-Path $Destination $SubFolder

    [PSCustomObject]@{
        'Head'       = $Head
        'Tail'       = $Tail
        'SubFolder'  = $SubFolder
        'FullPath'   = $FullPath
        'SubExists'  = Test-Path $FullPath
    }
} | Out-gridview

Оптимизация

Промежуточные переменные хороши во время разработки/тестирования/отладки, но могут замедлить работу внутри цикла, выполняемого так много раз. Поэтому, как только назначения стали надежными, я проделал обратный путь, сначала скопировав выражение, определяющее, $SubFolderв буфер обмена, затем заменив $SubFolderвхождение ' на $FullPath = ...его определение, заключенное в скобки, затем сделав то же самое для переменных внутри этого определения. Также объединил назначение с первым использованием. Результат был следующим:

Get-ChildItem -Path $Source -FIlter *.pdf -File | ForEach{
If ( ($BaseName = $_.BaseName) -match '_' ) {
        If ( Test-Path ( $FullPath=Join-Path $Destination "$($BaseName.Split('_')[-1])`_$($BaseName.Split('_')[0])" ) ) {
            Copy-Item -Literal $_.FullName $FullPath
        } Else {
            '"{0}" not copied because the directory "{1}" does not exist' -f $_.Name , $FullPath | Write-Output
        }
    }
}

Читать это чертовски сложно, но для больших наборов данных это того стоит.

Связанный контент