KopiŽren met checksums onder Windows

Door GrimaceODespair op zaterdag 12 oktober 2013 04:23 - Reacties (12)
Categorie: Batch files, Views: 4.694

Aangezien er geen kant-en-klare cheap-ass command-line oplossing onder Windows bestaat om bestanden van A naar B efficiŽnt te synchroniseren, werd het tijd om er een herbruikbaar script voor te bakken. Met behulp van het door de US Air Force (!) ontwikkelde MD5Deep wordt het al wat makkelijker om bijvoorbeeld build scripts snel te laten xcopy deployen.


Onderstaand script doet in essentie niet anders dan een gewone "xcopy /y /e". Exact dat commando wordt ook uitgevoerd wanneer er op de doellocatie nog geen checksums (md5.txt) te vinden zijn. Een gewone recursieve kopieeropdracht dus, met als belangrijk verschil dat ook de md5.txt wordt meegekopieerd.

Bij een volgende run wordt de bron vergeleken met het doel, en alleen veranderde bestanden worden nu overschreven.

Merk op dat checksums alleen van bronbestanden worden berekend. De correcte checksums van de doelbestanden bevinden zich altijd in het md5.txt-bestand op de doellocatie. Tenminste, als er niemand in de tussentijd die doelbestanden stiekem aanpast.

Het voordeel van deze aanpak, is dat het ook mogelijk is om over het netwerk te syncen zonder dat volledige bestanden moet binnengehaald worden, enkel om de checksum te berekenen.

Eat your heart out...


code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
@echo off

setlocal EnableDelayedExpansion

set MD5=D:\tools\md5\md5deep.exe
set SOURCE=%~1
set TARGET=%~2

if "%SOURCE%"=="" (
  echo Error: No source folder given
  goto syntax
)
:: Remove trailing backslash froms source
if "!SOURCE:~-1!"=="\" set SOURCE=!SOURCE:~,-1!

if "%TARGET%"=="" (
  echo Error: No target folder given
  goto syntax
)
:: Remove trailing backslash from target
if "!TARGET:~-1!"=="\" set TARGET=!TARGET:~,-1!


echo.
echo Copy files with checksum
echo  from: %SOURCE%
echo  to: %TARGET%
echo =====================

if not exist "%SOURCE%" (
  echo Error: path does not exist: %SOURCE%
  goto exit
)

if not exist "%SOURCE%\*" (
  echo Error: path is not a folder: %SOURCE%
  goto exit
)

if not exist "%TARGET%" (
  md "%TARGET%"
  if not exist "%TARGET%" (
    echo Error while creating target folder: %TARGET%
    goto exit
  )
)

pushd "%SOURCE%"

echo.
echo Calculating checksums
echo =====================

"%MD5%" -r -l *.* >"%SOURCE%\md5.txt"
type "%SOURCE%\md5.txt"

if not exist "%TARGET%\md5.txt" (

  echo.
  echo Creating fresh copies
  echo =====================
  xcopy /f /y /e "%SOURCE%" "%TARGET%" 
  if errorlevel 1 (
    echo Error while making fresh copy
    goto exit
  )
  
) else (

  echo.
  echo Copying new files
  echo =================
  
  for /r %%i in (*.*) do (
    set TARGETPATH=%%i
    set TARGETPATH=%TARGET%!TARGETPATH:%SOURCE%=!
    if not exist "!TARGETPATH!" (
      call :copyfile "%%i" "!TARGETPATH!" 
    )
  )
  
  echo.
  echo Copying changed files
  echo =====================
  
  for /f %%i in ('%MD5% -r -l -x "%TARGET%\md5.txt" *.*') do (
    call :copyfile "%SOURCE%\%%i" "%TARGET%\%%i"
  )
)

popd

goto exit

:copyfile
:: The * is a hack to prevent xcopy from querying for file or folder
xcopy /f /y %1 %2*
if errorlevel 1 (
  echo Error while copying
  goto exit
)
goto:eof

:return
exit /b %1

:syntax
echo.
echo %~nx0 copies files from a source to a target, and keeps track of MD5
echo hashes to copy only new and changed files on subsequent calls.
echo.
echo   Syntax: %~nx0 SourceFolder TargetFolder
echo.
goto exit

:exit
endlocal & call :return %errorlevel%

Volgende: Performance counters in C# 10-'13 Performance counters in C#
Volgende: FIPO 10-'13 FIPO

Reacties


Door Tweakers user MoBi, zaterdag 12 oktober 2013 08:40

Moet deze regel echo Cannot create target folder: %SOURCE%
geen %TARGET% zijn?

Door Tweakers user GrimaceODespair, zaterdag 12 oktober 2013 10:21

Dat is dan al een copy-paste-fout in mijn allereerste tech-blogpost. tnx :)

Door Tweakers user Rataplan_, zaterdag 12 oktober 2013 14:36

Wat is er mis met robocopy om op deze manier te syncen? Ik ben persoonlijk lang op zoek geweest naar een tool die zoals rsync onder linux daadwerkelijk enkel de delta synced, waarbij dus aan beide kanten een file gehashed wordt en zo de verschillen bepaald. Dit is niet echt voorhanden onder windows, zeker niet out-of-the-box. Maar robocopy biedt toch wel uitkomst. Die werkt welliswaar niet met hases maar met modified date en timestamp en kopieert net zoals jouw script bij verschillen de hele file. Tenzij je aan de sourcekant iets heel creatiefs doet kan je nooit een file wijzigen zonder daadwerkelijk de modified-stamp te wijzigen, waardoor robocopy prima voldoet en is standaard aanwezig in Windows (Vista+). Maw wat is toegevoegde waarde van dit tov Robocopy? (niet om te bashen, oprechte vraag)

Verder tof om te zien dat je gewoon batch gebruikt, ik ben daar een groot voorstander van omdat het nog steeds een krachtige taal en vooral leesbare taal is. Ik adviseer je wel om je script 'gereed' te maken voor padnamen met spaties door overal netjes " te gebruiken en niet alleen bij de xcopy opdracht. Ook op het eind van je script de variablen weer leeg maken, dat is wel zo netjes.

Door Tweakers user Blokker_1999, zaterdag 12 oktober 2013 18:11

rataplan, als je op zoek bent naar een tool als rsync, waarom dan niet gewoon rsync nemen? Er bestaan gelukkig ook windows versies van. Maak er op werk zelf gebruik van, maar kan me nu niet direct herinneren waar deze vandaan kwam.

Door Tweakers user real[B]art, zaterdag 12 oktober 2013 19:19

In plaats van SOURCE en TARGET hard te definiŽren in je script zou je die ook als parameter mee kunnen geven:

code:
1
script.bat c:\source d:\target



In je script vervang je dan %SOURCE% door %1 en %TARGET% door %2.

Door Tweakers user GrimaceODespair, zaterdag 12 oktober 2013 23:36

Rataplan_ schreef op zaterdag 12 oktober 2013 @ 14:36:
Wat is er mis met robocopy om op deze manier te syncen? [...] Die werkt welliswaar niet met hases maar met modified date en timestamp en kopieert net zoals jouw script bij verschillen de hele file. [...]. Maw wat is toegevoegde waarde van dit tov Robocopy? (niet om te bashen, oprechte vraag)
De use case voor dit script tov tools die werken met timestamps, zijn build processen waar de schrijfdatum van bestanden soms afhankelijk is van het moment van builden. Een binary kan opnieuw gebouwd zijn of gekopieerd op een manier die de timestamp niet waarborgt.

Als je in zo een scenario hashes inzet, kan je voorkomen dat je nodeloos bestanden uploadt. Ik maak dan wel de aanname dat er niemand op de doellocatie handmatig gaat rotzooien, maar die randvoorwaarde is in dergelijke scenario's vaak vervuld.
Rataplan_ schreef op zaterdag 12 oktober 2013 @ 14:36:
Ik adviseer je wel om je script 'gereed' te maken voor padnamen met spaties door overal netjes " te gebruiken en niet alleen bij de xcopy opdracht. Ook op het eind van je script de variablen weer leeg maken, dat is wel zo netjes.
Ik heb inderdaad weinig aandacht besteed aan het correct quoten van de paden, omdat spaties in mijn geval niet voorkwamen. Zal dit nog even proberen te fixen.

En wat betreft het opruimen van variabelen: imho volstaat het gebruik van setlocal/endlocal, of zie ik dan iets over het hoofd?
Blokker_1999 schreef op zaterdag 12 oktober 2013 @ 18:11:
rataplan, als je op zoek bent naar een tool als rsync, waarom dan niet gewoon rsync nemen? Er bestaan gelukkig ook windows versies van. Maak er op werk zelf gebruik van, maar kan me nu niet direct herinneren waar deze vandaan kwam.
De laatste keer dat ik daarnaar gekeken heb (is wel al even geleden), had ik volgens mij alleen de optie om cygwin te gebruiken. Dat wou ik eigenlijk vermijden.

En verder: ik wil geen extra service draaien. Voor zover ik weet, kan rsync met file hashes werken, maar als er geen rsync-service op het doel draait, zouden deze hashes dus vanaf de bronlocatie over het netwerk heen berekend moeten worden. Wat dus weer zijn doel voorbijschiet omdat dan toch weer het hele bestande over het netwerk moet.
real[B]art schreef op zaterdag 12 oktober 2013 @ 19:19:
In plaats van SOURCE en TARGET hard te definiŽren in je script zou je die ook als parameter mee kunnen geven:

code:
1
script.bat c:\source d:\target



In je script vervang je dan %SOURCE% door %1 en %TARGET% door %2.
Ik had het mezelf even gemakkelijk gemaakt, omdat ik geen normalisatie van parameters wou doen, maar je hebt natuurlijk helemaal gelijk :)

Door Tweakers user GrimaceODespair, zondag 13 oktober 2013 09:17

Script geŁpdatet om te werken met parameters die bovendien extra gecheckt worden. Ook quoting toegevoegd. Bug reports welkom ;)

[Reactie gewijzigd op zondag 13 oktober 2013 09:17]


Door Tweakers user Blokker_1999, zondag 13 oktober 2013 10:49

Een cygwin installatie op zich is niet noodzakelijk om met rsync op windows te werken, de nodige dlls komen gewoon mee.

Door Tweakers user Rataplan_, zondag 13 oktober 2013 14:02

Blokker_1999 schreef op zaterdag 12 oktober 2013 @ 18:11:
rataplan, als je op zoek bent naar een tool als rsync, waarom dan niet gewoon rsync nemen? Er bestaan gelukkig ook windows versies van. Maak er op werk zelf gebruik van, maar kan me nu niet direct herinneren waar deze vandaan kwam.
Er is o.a. Deltacopy, maar alle rsync ports hebben gemeen dat ze onder cygwin draaien en derhalve posix permissions gebruiken en niet overweg kunnen met de Windows / NTFS ACLs (security op bestanden dus). Voor de toepassing waar ik het voor nodig had zijn de permissions cruciaal, en dus ben ik uiteindelijk voor robocopy gegaan. Er is nog Syncrify van dezelfde makers als deltacopy, welke wel Windows / NTFS permissions meeneemt maar die is betaald en dat was niet mijn insteek,

Door Tweakers user Rataplan_, zondag 13 oktober 2013 14:04

Rataplan_ schreef op zondag 13 oktober 2013 @ 14:02:
[...]


Er is o.a. Deltacopy, maar alle rsync ports hebben gemeen dat ze onder cygwin draaien en derhalve posix permissions gebruiken en niet overweg kunnen met de Windows / NTFS ACLs (security op bestanden dus). Voor de toepassing waar ik het voor nodig had zijn de permissions cruciaal, en dus ben ik uiteindelijk voor robocopy gegaan. Er is nog Syncrify van dezelfde makers als deltacopy, welke wel Windows / NTFS permissions meeneemt maar die is betaald en dat was niet mijn insteek,
En wat betreft het opruimen van variabelen: imho volstaat het gebruik van setlocal/endlocal, of zie ik dan iets over het hoofd?
je hebt helemaal gelijk, heb ik denk ik overheen gelezen.

Door Tweakers user Permutor, zondag 13 oktober 2013 20:44

Het md5deep.exe programma gooit een waarschuwing als je een 64-bit versie van het operating system gebruikt:
md5deep.exe: WARNING: You are running a 32-bit program on a 64-bit system.
md5deep.exe: You probably want to use the 64-bit version of this program.


Het is daarom nuttig het script uit te breiden, en afhankelijk van de bitness de 32-bit (md5deep.exe) of 64-bit versie (md5deep64.exe) aan te roepen.

De bitness is op verschillende manieren te achterhalen.
Vaak zie een test op het bestaan van directory "C:\Program Files (x86)"
Maar je kunt het ook afleiden uit de output van
wmic os get osarchitecture
OSArchitecture
64-bit

[Reactie gewijzigd op zondag 13 oktober 2013 20:47]


Door Tweakers user GrimaceODespair, zondag 13 oktober 2013 23:58

Permutor schreef op zondag 13 oktober 2013 @ 20:44:
Het is daarom nuttig het script uit te breiden, en afhankelijk van de bitness de 32-bit (md5deep.exe) of 64-bit versie (md5deep64.exe) aan te roepen.
Leuk idee, maar de vraag is hoe nuttig. Immers: het pad naar de md5-tool moet toch al in het script aangepast worden (tenzij de hele wereld de volledige md5deep-suite altijd in d:\tools\md5 heeft staan), en dat kan je dus laten verwijzen naar de 32-bit dan wel de 64-bit versie die je ergens neerplempt.

Laat ik me ervanaf maken met te stellen dat het buiten de scope van dit script valt ;)

Reageren is niet meer mogelijk