3

Basically, in an automated batch file, I want to copy and rename a file if the destination file is missing or older. There are several variants, which do not quite work:

copy /Y c:\source\a.file c:\dest\b.file 

- always copies, no /D option or something

xcopy /Y /D c:\source\a.file c:\dest\b.file

- if destination does not exist, tries to ask if the destination is a file or directory, creating havoc in the automated build.

robocopy /XO ... 

- does not support renaming the file.

It seems Windows has not managed to replicate Unix "cp -u" in 20+ years, or am I overlooking something?

  • 1
    I don't think replicating Unix is any kind of priority for Windows. – Der Hochstapler Nov 8 '13 at 10:24
  • 1
    Yes, a better wording would have been 'replicating functionality of "cp -u"'. – Paavo Nov 8 '13 at 10:42
  • 2
    You could use echo f | xcopy /Y /D c:\source\a.file c:\dest\b.file ;) But do make sure it's an English Windows-version. – Rik Nov 8 '13 at 10:47
  • Have you tried using the /I option with xcopy? – selbie Nov 8 '13 at 12:00
2

Create a file containing one line with the letter f in it (call it whatever you want, for this answer it will be f.txt).

Pipe the contents of f.txt to the xcopy command :

xcopy /Y /D c:\source\a.file c:\dest\b.file<f.txt

This will supply the answer to the (F = file, D = directory)? prompt that occurs if a file doesn't exist.

**Edit:**Just realized this basically duplicates the functionality of Rik's comment to the question, only his answer is more concise.

  • or echo f | xcopy a.txt b.txt /Y /D – Junior M Dec 29 '14 at 20:39
2

This may be more than you need but with the proper tweaking it should do the job. I did some experimenting and it seems that if you use xcopy with the /D option and specify a destination filename, you will get a copy whether the files are the same or not. (Seems like it's basing the compare off the destination filename instead of the source.) So I only used xcopy to do the comparison but not actually to do the copy. (Thanks selbie for suggesting the /L option.) It seems rather large but if you remove the comments it's pretty small. This example copies and renames files that exist in the destination but are newer using the current date and time. It copies but does not rename files that don't exist in the destination (can be easily changed). And does not copy files that are the same. I can help you modify it to fit your specific needs.

@ECHO OFF
REM ### Note that this assumes all filenames end with ".txt".  This can be changed to suit your needs ###

REM ### Needed so the variables will expand properly within the FOR DO loops ###
Setlocal EnableDelayedExpansion

REM ### Set the source file(s) and call the "CopyIt" subroutine ###
SET Sourcefile=Test 1.txt
CALL :CopyIt

SET Sourcefile=Test2.txt
CALL :CopyIt

REM ### End of the program.  Otherwise the subroutine will run again ###
REM ### You can also just put an "EXIT" command here ###
GOTO END

REM ### The subroutine ###
:CopyIt
    REM ### Set the date and time to a variable called "DaTime" and remove offending ###
    REM ### characters (/ and :) that can't be used in a filename ###
    FOR /f "tokens=1-4 delims=/ " %%a in ("!date!") do SET DaTime=%%b-%%c-%%d
    FOR /f "tokens=1-3 delims=':'" %%e in ("!time!") do SET DaTime=!DaTime!_%%e-%%f-%%g

    REM ### Set a variable called "DestFile" to source filename minus ".txt" ###
    REM ### to be used to set the new destination file name + "DaTime" ###
    SET Destfile=!Sourcefile:.txt=!

    REM ### Check to see if the source filename exists in the destination directory ###
    DIR "C:\_Dest\!Sourcefile!" > NUL
    IF !ErrorLevel!==0 (
        REM ### If it does exist, set the loop count to 0 ###
        SET /a Count=0
        REM ### Have xcopy check to see if the source file is newer, but only report and not copy ###
        REM ### Thanks to selbie for suggesting the /L option ###
        FOR /f "tokens=1 delims=0" %%s in ('xcopy /Y /D /L "C:\_Source\!Sourcefile!" "C:\_Dest\"') DO (
            REM ### Increment the loop count ###
            SET /a Count=!Count!+1
            REM ### Only pick up the first iteration.  If the files are different, it will be the full path and the filename ###
            If !Count!==1 SET Source=%%s
        )
        REM ### If the source is the same as the destination, then !Source! will = " File(s)".  Change it to NONE ###
        SET Source=!Source: File^(s^)=NONE!
        REM ### If it's not equal to NONE, copy the file and rename it to source file + date & time + .txt ###
        IF !Source! NEQ NONE COPY /Y "!Source!" "C:\_Dest\!Destfile!-!DaTime!.txt"
    ) ELSE (
        REM ### If it does not exist, copy the file without renaming it ###
        COPY /Y "C:\_Source\!Sourcefile!" "C:\_Dest\"
    )
REM ### Exit the subroutine ###
EXIT /b

:END
pause
  • A thorough solution. Do all the variables in the subroutine really need delayed expansion? They're not inside the FOR command and can be evaluated as normal % delimited variables, I think. Also, could be simplified using IF EXISTS rather than DIR then checking ERRORLEVEL, I think. – Richard A Apr 17 '18 at 5:26
  • Also simplify by using GOTO :EOF rather than GOTO END and get rid of :END routine. – Richard A Apr 17 '18 at 5:33
  • Also, can use += when incrementing count. – Richard A Apr 17 '18 at 5:37
0

Maybe this:

xcopy /I /Y /D c:\source\a.file c:\dest\b.file
  • No, this still displays interactive prompt if the destination does not exist. – Paavo Nov 8 '13 at 13:52
  • /I only works if copying more than one file, in my experience. – Richard A Apr 17 '18 at 5:27
0

You can do this with standard copy and paste in Explorer on Windows 8, and 7 I think.

  1. Copy the files to the folder with the 'possible duplicates'
  2. When the copy prompts which copy to keep, tick the box at the bottom saying skip files that have same date and size
  3. Tick the box to select all in the source to keep and tick all the destination to keep

Explorer will then copy the files that have the same name but will add a number to the newly copied.

  • I guess you missed the first line of the question where this needed to be done in an automated batch file without user interaction. – Rik May 17 '15 at 7:31
0

You can try this:

xcopy "c:\source\a.file" "c:\dest\b.file*" /A /Y 
  • /Y remove prompt overwritting
  • "*" at the end of new file to remove prompt "File or Directory"

Good luck!

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.