
У меня на самом деле нет реальных точек соприкосновения с 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_99
→C:\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
Эта команда создаст папку, если ее нет, а затем вставит файл, а если файл существует внутри папки, он будет перезаписан.
ОБНОВЛЯТЬ:
Команда для поиска имени файла без расширения(и добавить результат в 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
}
}
}
Читать это чертовски сложно, но для больших наборов данных это того стоит.