From a source directory structure (multiple folders), I need to copy all csv files to a single target directory. I found a command to do this, though files of the same name exist within different folders of the source structure, causing obvious issues when copied to one folder.

How can the files with duplicate names be renamed during the copy please (ideally: report.csv, reportcopy2.csv etc)? The job currently only copies a single instance of each file. Thanks for help.


This is a slight work around - I know you wanted to name differently but I wondered if this will work for you.

I would suggest you rename on copy to something like Foldername.Filename.csv.

Use something like

echo f | xcopy /f /y srcfile destfile


I have tried for a few hours, I don't think what you want is possible with the CMD prompt or bat files.

This is what I have

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 C:\Documents and Settings\drook\Desktop\Folder\
    ren "%%a" "newName-%dir%.txt"
    cd C:\Documents and Settings\drook\Desktop\


It fails on the rename because it ignores the variable. So where I'm showing newName-%dir% (where dir is the variable) it would fail on newName-%%a as well...

Sorry, I don't think it is possible.

Having said that, this makes it look like it is possible: windows-batch-file-to-copy-and-keep-duplicates

  • Thanks Dave. At the moment I am using FOR /R "src" %%a in ("*.csv") DO xcopy /d /y "%%a" "dest". Unsure how to integrate a name-change. Thanks
    – Nick
    Aug 29 '12 at 10:50
  • A bit more info - around 500 files from 3000 will be copied initially with perhaps groups of four or five with the same.
    – Nick
    Aug 29 '12 at 10:57

This is similar to aflat's answer:

setlocal enableDelayedExpansion
set counter=1
for /r src %%F in (*.csv) do (
    copy %%F dest\%%~nF-!counter!%%~xF
    set /a counter=counter+1

This should do it:

@echo off
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

  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

What it does:

  • Lists all files matching pattern (most inner for) formatted as name?path
  • Sorts the list (by file name)
  • Parses sorted list line by line (outer for) and for each line (file)
    • if file name appears first time, copies it unmodified
    • if file name repeats, adds suffix and count_nr to a copy

As it is now it will just echo copy command instead of executing it, so you can safely run it and check it's output. When you're ready, just remove echo from
echo copy "%~2" "%targetPath%\%~n1%sufix%%~x1%"


A simple PowerShell-Function can also do this:

Function Start-FileCopyWDupliCheck(){
    # specify input-path, output-path, renaming-scheme, and file-extension here:
        [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
            $j = 1
            Write-Host "$inter found in output-path -> appending `"$($appendix)XY`"..." -ForegroundColor Yellow
                $inter = "$path_out\$($file_input_name[$i])$appendix$j$($file_input_ext[$i])" -replace '[[+]*?()\\.]','\$&'
                if((Test-Path -LiteralPath $inter) -eq $true){
                    Write-Host "$appendix$j is working" -ForegroundColor Green
                    $ready_for_copying = $inter
        # finally, copy the file:
        Copy-Item -LiteralPath $($file_input_path[$i]) -Destination $ready_for_copying


Of course one could remove the "write-host"-commands - they simply provide some visual feedback. Also, it would be possible to first gather the file-names and then later copy all files in a separate loop, possibly speeding things up.

  • simply make $ready_for_copying an array,
  • add (+=) the $inter-values to it,
  • add the check for already specified output-names in the if-conditions (e.g. $inter -in $ready_for_copying),
  • then move the copy-item-command to a new for-block (e.g. for($i = 0; $i -lt $ready_for_copying.Length; $i++)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.