1

For batch copy purposes I need to create script that I was thinking will be simple, but no luck.

Use case is: Copy all files from sourcedirectory to targetdirectory where sourcedirectory file is newer of not exists in targetdirectory, but if file already exists in targetdirectory rename it to original_name.B<timestamp_last_modified> before copy/overwrite

What I started with:

xcopy %sourcedirectory% %targetdirectory% /f /i /s /h /d /y >> W:\Xcopy%datetimefile%.log 2>&1

But it can not do the rename part so I started playing around and got here:

@echo off
rem configuration
set sourcedirectory="d:\Ice"
set targetdirectory="W:\Ice"
set logdirectory="W:"

rem following part only creates timestamp for log file (datetimefile) and log record (datetimecopy)
for /f "tokens=2 delims==" %%I in ('wmic os get localdatetime /format:list') do set datetimefile=%%I
set datetimecopy=%datetimefile:~0,8% %datetimefile:~8,2%:%datetimefile:~10,2%:%datetimefile:~12,2%
set datetimefile=%datetimefile:~0,8%_%datetimefile:~8,6%
set "logfile=%logdirectory:~0,-1%\Backup%datetimefile%.log^""

rem logging
echo Log File is %logfile%
echo -----     BACKUP FROM: %sourcedirectory% TO: %targetdirectory% AT: %datetimecopy%     -----
echo -----     BACKUP FROM: %sourcedirectory% TO: %targetdirectory% AT: %datetimecopy%     ----- >> %logfile% 2>&1

rem execution
for /F "tokens=*" %%A in ('xcopy %sourcedirectory% %targetdirectory% /f /i /s /h /d /y /l') do (call :subroutine "%%A")

rem log timestam creation and logging
for /f "tokens=2 delims==" %%I in ('wmic os get localdatetime /format:list') do set datetimecopy=%%I
set datetimecopy=%datetimecopy:~0,8% %datetimecopy:~8,2%:%datetimecopy:~10,2%:%datetimecopy:~12,2%
echo -----     BACKUP FINISHED AT: %datetimecopy%     -----
echo -----     BACKUP FINISHED AT: %datetimecopy%     ----- >> %logfile% 2>&1

goto :finish

:subroutine
 set "f=%1"
 set "f=%f:~1,-1%"
 set "filefrom=%f: -> =" & set "fileto=%"
 set "filefrom=^"%filefrom%^""
 set "fileto=^"%fileto%^""
 if NOT EXIST %fileto% GOTO :cpyonly
 set "g=%fileto%"
 call set "newg=%%g:\=\\%%"
 for /f "tokens=2 delims==" %%N in ('wmic datafile where name^=%%newg%% get LastModified /format:list') do set lastmod=%%N
 set "i=%fileto%"
 set "target="%%~ni%%~xi.B%lastmod:~0,14%^""
 echo rename %g% %target%
 echo "rename %g% %target%" >> %logfile% 2>&1
 rename %g% %target% >> %logfile% 2>&1
 goto :cpyonly

:cpyonly
 echo "xcopy %filefrom% %fileto% /f /h /d /y"  >> %logfile% 2>&1
 echo xcopy %filefrom% %fileto% /f /h /d /y
 xcopy %filefrom% %fileto% /f /h /d /y >> %logfile% 2>&1
 GOTO :finish

rem :testing
rem wmic datafile where name="d:\\CopyFrom\\drziaky.txt" get LastModified /format:list
rem d:\Ice\Private\StartBatch\Backup_new.bat

:finish

However it did not work and can not move forward, the observed issues:

  1. The filenames contain spaces and it creates splits in the files even when I have used the "tokens=*" I have not found a way how to pass the whole %%A to subroutine
  2. By the file operations I sometimes need to remove or add the "" around, this works on some positions and on some not, again I failed to discover when/why (for example this set "f=%f:~1,-1%" is intended to remove " and this set "fileto=^"%fileto%^"" is intended to add them

To the previous versions (not in this post, but have them on my HDD):

  1. I tried using copy, but copy did not create target directory
  2. The wmic commands are used because of local differences and seems to work (for getting time and also last modification date)
  3. xcopy as list generator works (small issue with last line listing number of files)
  4. Parsing tried first via for /f and then via the set "filefrom=%f: -> =" & set "fileto=%" (copied from here) but none works

Below is last working code (with copy) but:

  • can not create target directories
  • Breaks on spaces in file name
  • Breaks on last line of xcopy (X files copied)

Maybe it breaks on some other cases also, but it is the best I managed to get until now, however get stuck and can not move forward from this point, could somebody help me? Thank you and apologize for long post. If it is already here then I'm really blind because have spend lot of hours searching.

PS: enabledelayedexpansion can not be used :-( Also usage of 3rd parties can not be relyed to so the script can be only 1 file

set sourcedirectory="d:\Ice"
set targetdirectory="W:\Ice"
set logdirectory="W:"
for /f "tokens=2 delims==" %%I in ('wmic os get localdatetime /format:list') do set datetimefile=%%I
set datetimecopy=%datetimefile:~0,8% %datetimefile:~8,2%:%datetimefile:~10,2%:%datetimefile:~12,2%
set datetimefile=%datetimefile:~0,8%_%datetimefile:~8,6%
set "logfile=%logdirectory:~0,-1%\Backup%datetimefile%.log^""
echo -----     BACKUP FROM: %sourcedirectory% TO: %targetdirectory% AT: %datetimecopy%     ----- >> %logfile% 2>&1

for /f "tokens=1,3" %%A in ('xcopy %sourcedirectory% %targetdirectory% /f /i /s /h /d /y /l') do (call :subroutine "%%A" "%%B")

for /f "tokens=2 delims==" %%I in ('wmic os get localdatetime /format:list') do set datetimecopy=%%I
set datetimecopy=%datetimecopy:~0,8% %datetimecopy:~8,2%:%datetimecopy:~10,2%:%datetimecopy:~12,2%
echo -----     BACKUP FINISHED AT: %datetimecopy%     ----- >> %logfile% 2>&1

goto :eof

:subroutine
 set "f=%1"
 if NOT EXIST %2 GOTO :cpyonly
 set "g=%2"
 call set "newg=%%g:\=\\%%"
 for /f "tokens=2 delims==" %%N in ('wmic datafile where name^=%%newg%% get LastModified /format:list') do set lastmod=%%N
 set "target="%~n2%~x2.B%lastmod:~0,14%^""
 echo "rename %g% %target%" >> %logfile% 2>&1
 rename %g% %target% >> %logfile% 2>&1
 goto :cpyonly

:cpyonly
 echo "copy %1 %2" >> %logfile% 2>&1
 copy %1 %2 >> %logfile% 2>&1
 GOTO :eof

:eof
aschipfl
  • 33,626
  • 12
  • 54
  • 99
Ice Planet
  • 11
  • 1
  • 2
    Why in the world can delayed expansion not be used? What version of Windows are you using? – SomethingDark May 30 '17 at 00:25
  • If ENABLEDELAYEDEXPANSION cannot be used, the I assume that suggesting that it would be pretty easy in PowerShell is out of the question. Right? – lit May 30 '17 at 01:50
  • @lit You are right, another thing the OP can try is `call set` –  May 30 '17 at 03:39
  • To avoid delayed expansion (for whatever reason) you can use sub-routine `call`s; for example, you have a `for` loop and you manipulate *and* read variables inside, so put the body into a sub-routine using normal percent-expansion and put `call` into the `for` loop... – aschipfl May 30 '17 at 09:48

1 Answers1

0

So finally after several hours I have found solution and now it works as I have expected, also added ziping of older files. If someone want to use feel free. How it works is that it checks all files and the files that did not exist on target are copied to target, if file exists on target it is renamed to date of last modification and ziped (here you need to setup own zipper and parameters), then new file is copied over, at the end xcopy is once more executed as safety measure in case my script will fail for certain files.

@echo off

for /f "tokens=2 delims==" %%I in ('wmic os get localdatetime /format:list') do set datetimefile=%%I
set datetimecopy=%datetimefile:~0,8% %datetimefile:~8,2%:%datetimefile:~10,2%:%datetimefile:~12,2%
set datetimefile=%datetimefile:~0,8%_%datetimefile:~8,6%
set "logfile=%logdirectory:~0,-1%\Bcopy%datetimefile%.log^""
set "logxcpy=%logdirectory:~0,-1%\Xcopy%datetimefile%.log^""

set sourcedirectory="d:\myDir"
set targetdirectory="w:\myDir"
set logdirectory="W:"
set whereiszip="C:\Program Files\7-Zip\7z.exe"

echo -----   BACKUP FROM: %sourcedirectory% TO: %targetdirectory% AT: %datetimecopy% LOG: %logfile% ZIP: %whereiszip% START   -----
echo -----   BACKUP FROM: %sourcedirectory% TO: %targetdirectory% AT: %datetimecopy% LOG: %logfile% ZIP: %whereiszip% START   ----- >> %logfile% 2>&1

for /f "tokens=*" %%A in ('xcopy %sourcedirectory% %targetdirectory% /f /i /s /h /d /y /l') do (call :subroutine "%%A")
xcopy %sourcedirectory% %targetdirectory% /f /i /s /h /d /y >> %logxcpy% 2>&1

for /f "tokens=2 delims==" %%I in ('wmic os get localdatetime /format:list') do set datetimefile=%%I
set datetimecopy=%datetimefile:~0,8% %datetimefile:~8,2%:%datetimefile:~10,2%:%datetimefile:~12,2%
echo -----   BACKUP FROM: %sourcedirectory% TO: %targetdirectory% AT: %datetimecopy% LOG: %logfile% ZIP: %whereiszip% FINISH   -----
echo -----   BACKUP FROM: %sourcedirectory% TO: %targetdirectory% AT: %datetimecopy% LOG: %logfile% ZIP: %whereiszip% FINISH   ----- >> %logfile% 2>&1

if "%1"=="" goto :finish

if "%1"=="S" shutdown /s
if "%1"=="M" goto :outlook
if "%1"=="O" goto :outlook


:subroutine
if "%~1"=="" goto :cannotparse
set str1=%1
for /f "tokens=2 delims==" %%I in ('wmic os get localdatetime /format:list') do set datetimefile=%%I
set datetimecopy=%datetimefile:~0,8% %datetimefile:~8,2%:%datetimefile:~10,2%:%datetimefile:~12,2%
if not defined str1 goto :cannotparse
if x%str1: -> =%==x%str1% goto :cannotparse
echo string %str1% contains -^>
set str=%1
@setlocal enableextensions enabledelayedexpansion
set "find= -> "
call set last=%%str:*!find!=%%
call set first=%%str:!last!=%%
call set first=%%first:!find!=%%
call set first=!first!^"
call set last=^"!last!
set nqlast=!last:~1,-1!
@setlocal enableextensions disabledelayedexpansion
for %%A in ("%nqlast%") do (
    call set folder=%%~dpA
    call set file=%%~nxA
)
echo %datetimecopy% INPUT: [%str%] source file: [%first%] destination file: [%last%] path: [%folder%] name: [%file%]
echo %datetimecopy% INPUT: [%str%] source file: [%first%] destination file: [%last%] path: [%folder%] name: [%file%] >> %logfile% 2>&1
if NOT EXIST %last% GOTO :cpyonly
set "g=%last%"
call set "newg=%%g:\=\\%%"
for /f "tokens=2 delims==" %%N in ('wmic datafile where name^=%%newg%% get LastModified /format:list') do set lastmod=%%N
set "target="%file%.B%lastmod:~0,14%^""
echo %datetimecopy% RENAME rename %last% %target%
echo %datetimecopy% RENAME rename %last% %target% >> %logfile% 2>&1
rename %last% %target% >> %logfile% 2>&1
set "target="%folder%%file%.B%lastmod:~0,14%^""
set "ztarget="%folder%%file%.B%lastmod:~0,14%.zip^""
if EXIST %whereiszip% (
    echo %datetimecopy% 7ZIP: %whereiszip% a -tzip -mx9 %ztarget% %target%
    echo %datetimecopy% 7ZIP: %whereiszip% a -tzip -mx9 %ztarget% %target% >> %logfile% 2>&1
    %whereiszip% a -tzip -mx9 %ztarget% %target% >> %logfile% 2>&1
    if %ERRORLEVEL% EQU 0 (
        echo %datetimecopy% DELETE: del %target%
        echo %datetimecopy% DELETE: del %target% >> %logfile% 2>&1
        del %target% >> %logfile% 2>&1
    )
)
goto :cpyonly
endlocal
goto :finish

:cpyonly
set "quotedfolder="%folder%^""
echo %datetimecopy% XCOPY: xcopy %first% %quotedfolder% /f /i /s /h /d /y
echo %datetimecopy% XCOPY: xcopy %first% %quotedfolder% /f /i /s /h /d /y >> %logfile% 2>&1
xcopy %first% %quotedfolder% /f /i /s /h /d /y >> %logfile% 2>&1
goto :finish

:cannotparse
echo %datetimecopy% ERROR: string %str1% did NOT contains -^>
echo %datetimecopy% ERROR: string %str1% did NOT contains -^> >> %logfile% 2>&1
goto :finish

:outlook
c:
cd "\Program Files\Microsoft Office\Office12\"
start OUTLOOK.EXE /recycle
goto :finish

:finish
endlocal
endlocal
Ice Planet
  • 11
  • 1
  • Have found issue that * in case there is certain form of quotes used xcopy transform these quotes to another quotes * in case there are file names containing & the script breaks :-( – Ice Planet Jul 18 '17 at 15:14