Вопрос или проблема
Из структуры каталога-источника (несколько папок) мне нужно скопировать все файлы csv в одну целевую директорию. Я нашел команду для этого, но файлы с одинаковыми именами существуют в разных папках структуры источника, что вызывает очевидные проблемы при копировании в одну папку.
Как переименовать файлы с дублирующимися именами во время копирования (идеально: report.csv, reportcopy2.csv и т. д.)? В настоящее время работа копирует только один экземпляр каждого файла. Спасибо за помощь.
Это небольшой обходной путь — я знаю, что вы хотели назвать по-другому, но подумал, подойдет ли это вам.
Я бы предложил переименовать при копировании что-то вроде Foldername.Filename.csv.
Используйте что-то вроде
echo f | xcopy /f /y srcfile destfile
ИЗМЕНИТЬ
Я пытался несколько часов, не думаю, что то, чего вы хотите, возможно с помощью CMD-промпта или bat-файлов.
Вот что у меня есть
set sDir=C:\Documents and Settings\drook\Desktop
set dDir="C:\Documents and Settings\drook\Desktop\Folder\"
cd C:\Documents and Settings\drook\Desktop\
FOR /F %%a in ("*.txt") DO (
xcopy "%%a" "C:\Documents and Settings\drook\Desktop\Folder\"
cd\
cd C:\Documents and Settings\drook\Desktop\Folder\
ren "%%a" "newName-%dir%.txt"
cd\
cd C:\Documents and Settings\drook\Desktop\
)
pause
Не удается переименовать, потому что игнорируется переменная. Поэтому, когда я показываю newName-%dir% (где dir — переменная), он также не сработает на newName-%%a…
Извините, не думаю, что это возможно.
Сказав это, это выглядит так, как будто это возможно: windows-batch-file-to-copy-and-keep-duplicates
Это похоже на ответ aflat:
setlocal enableDelayedExpansion
set counter=1
for /r src %%F in (*.csv) do (
copy %%F dest\%%~nF-!counter!%%~xF
set /a counter=counter+1
)
Это должно сработать:
@echo off
setlocal
set "sourcePath=c:\source"
set "targetPath=c:\temp\target"
set "pattern=*.csv"
set prev=""
set count=0
for /f "tokens=1,2 delims=?" %%F in ('^(for /r "%sourcePath%" %%N in ^("%pattern%"^) do ^@echo %%~nxN?%%N?^)^|sort') do (
call :copyFile "%%F" "%%G"
set prev="%%F"
)
goto :eof
:copyFile
if /I %prev%==%1 (set /a count+=1) else (set count=0)
if %count%==0 (set sufix=) else (set sufix=Copy%count%)
echo copy "%~2" "%targetPath%\%~n1%sufix%%~x1%"
goto :eof
Что это делает:
- Составляет список всех файлов, соответствующих шаблону (внутренний
for
) в формате name?path - Сортирует список (по имени файла)
- Анализирует отсортированный список построчно (внешний
for
) и для каждой строки (файла)- если имя файла появляется в первый раз, копирует его без изменений
- если имя файла повторяется, добавляет суффикс и номер копии
Сейчас он просто выводит команду копирования вместо выполнения, поэтому вы можете безопасно его запустить и проверить вывод. Когда будете готовы, просто удалите echo
из
echo copy "%~2" "%targetPath%\%~n1%sufix%%~x1%"
Простая функция PowerShell также может это сделать:
Function Start-FileCopyWDupliCheck(){
# specify input-path, output-path, renaming-scheme, and file-extension here:
param(
[string]$path_in = 'D:\Temp\bla [ ] pfad\[ ]',
[string]$path_out="D:\Temp\mirrbla [123] 123",
[string]$appendix = "_copy",
[string]$fileext = "*.*"
)
# get all the files (and the necessary attributes)
[array]$file_input = @(Get-ChildItem -LiteralPath $path_in -Filter $fileext -Recurse)
[array]$file_input_path = @($file_input | ForEach-Object {$_.FullName})
[array]$file_input_name = @($file_input | ForEach-Object {$_.BaseName})
[array]$file_input_ext = @($file_input | ForEach-Object {$_.Extension})
# check file-names for duplicates:
for($i = 0; $i -lt $file_input_path.Length; $i++){
$inter = "$path_out\$($file_input_name[$i])$($file_input_ext[$i])" -replace '[[+]*?()\\.]','\$&'
if((Test-Path -LiteralPath $inter) -eq $false){
Write-Host "$inter not found in output-path -> leaving the name the same" -ForegroundColor Green
$ready_for_copying = $inter
}else{
$j = 1
Write-Host "$inter found in output-path -> appending `"$($appendix)XY`"..." -ForegroundColor Yellow
while($true){
$inter = "$path_out\$($file_input_name[$i])$appendix$j$($file_input_ext[$i])" -replace '[[+]*?()\\.]','\$&'
if((Test-Path -LiteralPath $inter) -eq $true){
$j++
continue
}else{
Write-Host "$appendix$j is working" -ForegroundColor Green
$ready_for_copying = $inter
break
}
}
}
# finally, copy the file:
Copy-Item -LiteralPath $($file_input_path[$i]) -Destination $ready_for_copying
}
}
Start-FileCopyWDupliCheck
Конечно, можно удалить команды “write-host”, они просто обеспечивают некоторую визуальную обратную связь.
Также можно было бы сначала собрать имена файлов, а затем позже скопировать все файлы в отдельном цикле, что возможно ускорит процесс.
- просто сделайте
$ready_for_copying
массивом, - добавляйте (
+=
) в него значения$inter
, - добавьте проверку уже указанных имен выходных файлов в
if
-условия (например,$inter -in $ready_for_copying
), - затем переместите команду
copy-item
в новый блокfor
(например,for($i = 0; $i -lt $ready_for_copying.Length; $i++)
.
Ответ или решение
Чтобы решить задачу по копированию файлов с расширением .csv
из сложной структуры каталогов в одну папку с возможностью переименования файлов во избежание конфликта имен, можно использовать несколько подходов. Мы рассмотрим наиболее удобные и распространенные способы, а именно: использование командного файла Windows Batch, а также PowerShell-скрипта.
Решение с помощью Windows Batch
В Windows Batch можно создать скрипт, который будет проходить по всем подкаталогам, копировать файлы в целевой каталог и при необходимости переименовывать их. Для этого мы используем скрипт, который предоставлен в вопросе, но с небольшой модификацией для выполнения задачи:
@echo off
setlocal enableDelayedExpansion
rem Установка путей источника и назначения
set "sourcePath=C:\source"
set "targetPath=C:\destination"
rem Установка шаблона файла
set "pattern=*.csv"
rem Переменные для учета прошлого имени файла и счетчика
set prevName=""
set count=0
for /r "%sourcePath%" %%F in (%pattern%) do (
set "fileName=%%~nxF"
set "fileNameOnly=%%~nF"
set "fileExtension=%%~xF"
if /I "!fileName!"=="!prevName!" (
set /a count+=1
set "newFileName=!fileNameOnly!_copy!count!!fileExtension!"
) else (
set count=1
set "newFileName=!fileName!"
)
copy "%%F" "%targetPath%\!newFileName!"
set "prevName=!fileName!"
)
endlocal
Решение с помощью PowerShell
PowerShell предоставляет более развитые возможности для обработки файлов и манипуляции именами файлов. Ниже представлен скрипт, который выполнит аналогичную задачу:
Function Copy-CsvFilesWithRename {
param(
[string]$sourcePath = "C:\source",
[string]$targetPath = "C:\destination",
[string]$fileExtension = "*.csv"
)
if (-Not (Test-Path $targetPath)) {
New-Item -ItemType Directory -Path $targetPath
}
$files = Get-ChildItem -Path $sourcePath -Recurse -Filter $fileExtension
foreach ($file in $files) {
$baseName = $file.BaseName
$extension = $file.Extension
$destFile = Join-Path $targetPath ($baseName + $extension)
$i = 1
while (Test-Path $destFile) {
$destFile = Join-Path $targetPath ("$baseName" + "_copy$($i)" + $extension)
$i++
}
Copy-Item -Path $file.FullName -Destination $destFile
}
}
Copy-CsvFilesWithRename
Заключение
Оба предложенные подхода предназначены для автоматизации процесса копирования файлов с их переименованием в случае дублирования имен. Командный файл удобно использовать в среде Windows без дополнительных инструментов, однако PowerShell предлагает более мощный и гибкий инструмент для работы с файлами, что делает его предпочтительным выбором для более сложных задач.
Не забудьте изменить пути к файлам и структурам в предложенных скриптах на соответствующие вашему случаю. Перед выполнением скриптов в производственной среде убедитесь в их корректности и протестируйте их в безопасной тестовой среде.