Чужой опыт экономит время и увеличивает шансы для удачи

Бэкап с сохранением старых версий файлов и папок

Для Windows есть много разных программ для "зеркалирования" - создания точной резервной копии папки.
Но эти программы удаляют и перезаписывают старые версии файлов в бэкапе. А мне нужно было во время синхронизации не удалять старые версии файлов, а сохранять (например в заданную третью папку).
Когда просматривал разные известные программы для бэкапа, обнаружил, что обычно в них идея "третьей папки" вообще отсутствует. Есть две - исходная (откуда) и целевая (куда - "зеркальная"), а так, чтобы результат сравнения этих двух папок (отличающиеся старые или уже удаленные файлы) сохранялся в третью папку - такого вообщем-то нет.
Или почти нет.


Содержание

  1. "Третья папка" в программе CopyMik
  2. Другие варианты
  3. Robocopy - сохранение старых версий файлов [Решено]
  4. Интересные ключи Robocopy
  5. Некоторые особенности жестких ссылок Windows
  6. Дополнительный бэкап на другой компьютер

"Третья папка" в программе CopyMik

На самом деле есть такая программа. Называется - CopyMik.
Это своего рода расширенный вариант, более функциональная альтернатива известной Robocopy.

В CopyMik предварительное сохранение старых копий выглядит примерно так:

copymik.exe c:\откуда d:\куда /MIR /BEMPTY /BDEL d:\устаревшие /BREPL d:\устаревшие

c:\откуда - исходная папка-источник, d:\куда - целевая папка-приемник (бэкап-зеркало), d:\устаревшие - "третья папка".

Ключи:

/BDEL - Указывает папку для резервной копии удаляемых целевых файлов (напр. при ключе /MIR). Если папка не указана то используется головная целевая папка с постфиксом _BDEL и подпапка на основе {DATETIME} (см. ниже про переменные)
/BREPL - Указывает папку для резервной копии заменяемых целевых файлов (напр. при ключе /MIR или /OF ...). Если папка не указана то используется головная целевая папка с постфиксом _BREPL и подпапка на основе {DATETIME} (см. ниже про переменные)
/BEMPTY - Делать резервную копию удаляемых папок даже если они пустые (только в сочетании с ключами /BDEL и/или /MDEL и/или /EXMDEL)
* В названии "третьей папки" можно использовать переменные, например - {DATE} - текущая дата, {DATETIME} - текущие дата и время и др.

КСТАТИ! Эта программа может сразу "на лету" копируемые файлы сжимать и даже шифровать. Ключи: /CO - сжатие, /CR - шифрование, /B - сжатие с шифрованием, /CRPASS - пароль для шифрования (есть еще много других ключей с более тонкой настройкой этих функций).

ВАЖНО! Есть здесь пара настораживающих моментов. Во-первых функция предварительного сохранения удаляемых и измененных файлов и папок в данный момент присутствует только в последней альфа-версии (v2_20_9_alpha) (дополнение: уже в бэтте v2.21b2 - см. комментарий автора программы под этой статьей). Во-вторых несколько настораживает то, что программа хотя и бесплатная, но полностью закрытая (исходников нет) и мало что известно об ее авторе. Если Вас например это не смущает, то можно пользоваться этой программой - в ней именно то что нужно.

Другие варианты

Я очень долго (хотя может быть все-же недостаточно) искал другие варианты. Начиная от готовых решений до написания сложных скриптов на PowerShell.

По разным причинам мне ни одно из решений не понравилось. Либо оно было платным, либо громоздким, либо недостаточно функциональным (например не копирует атрибуты и права NTFS), либо непроверенным, ненадежным или даже подозрительным.

Вообще очень странно, что нет простого решения. Та же Robocopy - прекрасная программа, но в ней вообще нет идеи "третьей папки", а есть только две папки (исходная и целевая), и нет никаких возможностей перехвата, которые могли бы позволить например выполнить скрипт перед удалением отсутствующего или измененного файла в "зеркальной" (целевой) папке.

В результате принял решение использовать комбинацию Robocopy и vbs-скриптов.

Robocopy - сохранение старых версий файлов [Решено]

Рассмотренные скрипты проверялись только на последних версиях Windows. На XP и Vista необходима дополнительная проверка их работоспособности и некоторые корректировки (см. ниже).

ВАЖНО! Здесь рассматривается в основном частный случай того, что принято называть "бэкап" - т.е. только сохранение старых версий файлов и сохранение тех, которые были удалены. Полноценный бэкап - это когда данные сохраняются с дублированием, на отдельное физической устройство и производится еще много дополнительных действий.

Robocopy, .bat, .vbs и жесткие ссылки Windows

Рассмотренные скрипты проверялись только на последних версиях Windows.

Следующий скрипт (его нужно сохранить в текстовом файле с расширением .bat) запускается от имени Администратора.
Для корректной работы с кириллицей можно использовать например редактор "WordPad" в котором при сохранении выбрать "Сохранить как" -> "Текстовый документ MS-DOS" (если все работает и без этого, то этого делать не нужно).

ВАЖНО! Для того, чтобы создавались жесткие ссылки, необходимо чтобы папки "куда" и "устаревшие" находились на одном логическом диске. Кавычки обязательны именно там (и только там) где они указаны ниже.

Запускать так (в консоли):

"C:\_ПУТЬ_\backup.bat" "C:\откуда" "D:\куда" "D:\устаревшие" "D:\папка_скриптов"

В бэкапе участвуют три скрипта и программа Robocopy.
Скрипты hlinkch.vbs и dellink.vbs должны находится в папке D:\папка_скриптов .
Скрипт backup.bat в папке указанной в командной строке выше - C:\_ПУТЬ_\backup.bat

Центральный скрипт:

backup.bat

@echo off
rem chcp 866>nul
 
Set srcFolder=%1
Set destFolder=%2
Set saveFolder=%3
Set pathRunScript=%~4
 
echo -^> Source path		 %srcFolder%
echo -^> Destination path	 %destFolder%
echo -^> Save date Parent path %saveFolder%
echo.
 
echo Create hardlinks in new 'save date' folder (...Save_date_Parent_path\yyy_mm_dd):
for /f "tokens=*" %%i in ('cscript //Nologo "%pathRunScript%\hlinkch.vbs" /src:%srcFolder% /dest:%destFolder% /save:%saveFolder% /day:-2') ^
do set saveDateFolder=%%i
echo -^> 'Save Date' path -	[%saveDateFolder%]
echo.
 
rem if save date folder not created - skip 'save date' folder
IF NOT EXIST %saveDateFolder% GOTO SKIP
 
echo.
echo Fix attributes, time stamps, ntfs acl, owner, auditing for new hardlinks in:
echo -^> %saveDateFolder%
rem for old Windows --->>> robocopy %destFolder% %saveDateFolder% /E /COPY:ATSOU /XX /XL /IS /IT /ZB /R:3 /W:5
robocopy %destFolder% %saveDateFolder% /E /COPY:ATSOU /DCOPY:T /XX /XL /IS /IT /ZB /R:3 /W:5
 
echo.
echo Delete previous hardlink in destination folder:
for /f "tokens=*" %%i in ('cscript //Nologo "%pathRunScript%\dellink.vbs" /dest:%destFolder% /save:%saveDateFolder%') ^
do set countfiles=%%i
echo %countfiles% files deleted in destination - %destFolder%
 
GOTO MIR
:SKIP
echo.
echo Skip "save date" - folder name empty!
:MIR
 
echo.
echo Update (mirror) all from source to destination folder:
rem for old Windows --->>> robocopy.exe %srcFolder% %destFolder% /ZB /COPYALL /MIR /R:3 /W:5 /NP /LOG:"%saveFolder:~1%\backup.log"
robocopy.exe %srcFolder% %destFolder% /ZB /COPYALL /MIR /DCOPY:T /R:3 /W:5 /NP /UNILOG:"%saveFolder:~1%\backup.log"
echo.

Поиск измененных, тех что удалены и установка жестких ссылок:

hlinkch.vbs

Option Explicit
On Error Resume Next
Dim FSO, WshShell, testMode
Dim srcPath, destPath, savePath, savePathDate, saveDate, cmdMklink
Dim countChFolders, countChFiles
srcPath = Wscript.Arguments.Named.Item("src")
destPath = Wscript.Arguments.Named.Item("dest")
savePath = Wscript.Arguments.Named.Item("save")
saveDate = Now() + Wscript.Arguments.Named.Item("day")
If (Wscript.Arguments.Named.Exists("testmode")) Then
    testMode = True
Else
    testMode = False
End If
countChFolders = 0
countChFiles = 0
'cmdMklink = "fsutil hardlink create " ' for old Windows
cmdMklink = "mklink /h "
savePathDate = ""
Set WshShell = WScript.CreateObject("WScript.Shell")
'WshShell.Exec "cmd /c chcp 866"
Set FSO = CreateObject("Scripting.FileSystemObject")
If ((FSO.FolderExists(srcPath)) And (FSO.FolderExists(destPath)) _
   And (FSO.FolderExists(savePath))) Then
	savePathDate = NewDirBackup(Array(savePath, saveDate))
	If (Len(savePathDate) > 0) Then
	    MirrorDirFilesHlink(Array(srcPath, destPath, savePathDate, ""))
	    If ((countChFolders + countChFiles) > 0) Then
		' ...
	    Else
		If (testMode = False) Then 
		    FSO.DeleteFolder savePathDate, True
		End If
		savePathDate = ""
	    End If
	    If (testMode = True) Then 
		Wscript.Echo vbCrLf & "Foders: " & countChFolders & ", Files: " & countChFiles
		Wscript.Echo "Return 'save date' name new foder:"
	    End If
	End If
Else 
	If (testMode = True) Then
	    Wscript.Echo "Folder SOURCE or DESTINATION or SAVE not exist!!!"
	    Wscript.Echo "SOURCE -" & vbTab & srcPath
	    Wscript.Echo "DESTINATION -	" & destPath
	    Wscript.Echo "SAVE -" & vbTab & vbTab & savePath
	End If
End If
If (testMode = True) Then
Else
    Wscript.Echo Chr(34) & savePathDate & Chr(34)
End If 
 
Function NewDirBackup(dirNameArr)
    Dim fsoF, fsoFB
    Dim parentPath, saveDate, fldSaveDate
    parentPath = dirNameArr(0)
    saveDate = dirNameArr(1)
    Set fsoF = CreateObject("Scripting.FileSystemObject")
    If fsoF.FolderExists(parentPath) Then
	If (testMode = True) Then
	    Wscript.Echo parentPath & " <-- EXIST 'save' folder"
	End If
    Else
	If (testMode = True) Then
	    Wscript.Echo parentPath & " <-- NOT EXIST 'save' folder!!! Stopped!"
	End If
	WScript.Quit
    End If
    fldSaveDate = parentPath & "\" & _
	Year(saveDate) & "_" & Right("0" & Month(saveDate),2)  & "_" & _
	Right("0" & Day(saveDate),2)
    If fsoF.FolderExists(fldSaveDate) Then
	Set fsoFB = fsoF.GetFolder(fldSaveDate)
	if (fsoFB.SubFolders.Count > 0) Or (fsoFB.Files.Count > 0) Then 
	    If (testMode = True) Then
		Wscript.Echo fldSaveDate & " <-- 'save data' folder NOT EMPTY!!! Stopped!"
	    End If
	    NewDirBackup = ""
        Else
	    If (testMode = True) Then
		Wscript.Echo fldSaveDate & " <-- 'save data' folder EXIST and EMPTY" & vbCrLf
	    End If
	    NewDirBackup = fldSaveDate
        End If
    Else
	If (testMode = True) Then
            Wscript.Echo fldSaveDate & " <-- 'save date' folder CREATE!!!" & vbCrLf
	Else
	    fsoF.CreateFolder(fldSaveDate)
	End If
        NewDirBackup = fldSaveDate
    End If
End Function
 
Function MirrorDirFilesHlink(pDirArr)
' Out: testMode, countChFolders, countChFiles
    Dim srcP, destP, savPD, chldP
    Dim FSOmir, FSOmirDest
    Dim dstFolders, dstFolder, dstFiles, dstFile
    Dim srcPathFile, dstPathFile, savPathFile
    Dim childPath, childPathFile
    srcP = pDirArr(0)
    destP = pDirArr(1)
    savPD = pDirArr(2)
    chldP = pDirArr(3)
    Set FSOmir = CreateObject("Scripting.FileSystemObject")
    Set FSOmirDest = FSOmir.GetFolder(destP)
    Set dstFolders = FSOmirDest.SubFolders
    For Each dstFolder In dstFolders
	If (Len(chldP) > 0) Then 
	    childPath = chldP & "\" & dstFolder.Name
	Else 
	    childPath = dstFolder.Name
	End If
	If FSOmir.FolderExists(srcP & "\" & childPath) Then
	    If (compareFolders(Array(srcP & "\" & childPath, dstFolder.Path)) = True) Then
		If (testMode = True) Then
		    Wscript.Echo vbCrLf & "Skip Folder - " & srcP & "\" & childPath
		    Wscript.Echo "-> (i) EQUAL Folder: " & srcP & "\" & childPath & _
		    " = " & dstFolder.Path
		End If
	    Else
		createPathFolder(Array(savPD, Split(childPath, "\")))
		countChFolders = countChFolders + 1
		If (testMode = True) Then
		    Wscript.Echo "-> (i) DIFFERENT Folder in source: " & srcP & "\" & childPath
		End If
	    End If
	Else
	    createPathFolder(Array(savPD, Split(childPath, "\")))
	    countChFolders = countChFolders + 1
	    If (testMode = True) Then
		Wscript.Echo "-> (i) MISSING Folder in source: " & dstFolder.Path
	    End If
	End If
        MirrorDirFilesHlink(Array(srcP, destP & "\" & dstFolder.Name, savPD, childPath))
    Next
    Wscript.Echo ""
    Set dstFiles = FSOmirDest.Files
    For Each dstFile In dstFiles
	if (Len(chldP) > 0) Then
	    childPathFile = chldP & "\" & dstFile.Name
	Else
	    childPathFile = dstFile.Name
	End If
	srcPathFile = srcP & "\" & childPathFile
	dstPathFile = dstFile.Path
	savPathFile = savPD & "\" & childPathFile
	If FSOmir.FileExists(srcPathFile) Then
	    If (compareFiles(Array(srcPathFile, dstPathFile)) = True) Then
		If (testMode = True) Then
		    Wscript.Echo vbCrLf & "Skip FILE - " & dstPathFile
		    Wscript.Echo "->> (i) EQUAL FILE: " & srcPathFile & " = " & dstPathFile
		End If
	    Else
		createPathFolder(Array(savPD, Split(chldP, "\"))) ' maybe not exist save path folders?
		countChFiles = countChFiles + 1
		If (testMode = True) Then
		    Wscript.Echo "mklink " & savPathFile & " ==> " & dstPathFile
		    Wscript.Echo "->> (i) DIFFERENT FILE in source: " & srcPathFile
		Else
		    WshShell.Exec "cmd /c " & cmdMklink & Chr(34) & savPathFile & Chr(34) & _
		    " " & Chr(34) & dstPathFile & Chr(34)
		End If
	    End If
	Else
	    createPathFolder(Array(savPD, Split(chldP, "\"))) ' maybe not exist save path folders?
	    countChFiles = countChFiles + 1
	    If (testMode = True) Then
		Wscript.Echo "mklink " & savPathFile & " ==> " & dstPathFile
		Wscript.Echo "->> (i) MISSING FILE in source: " & srcPathFile
	    Else
		WshShell.Exec "cmd /c " & cmdMklink & Chr(34) & savPathFile & Chr(34) & _
		" " & Chr(34) & dstPathFile & Chr(34)
	    End If
	End If
    Next
End Function
 
Function createPathFolder(pathArr)
    Dim FSOp
    Dim parentPath, childPathArr
    Dim childPath, childPathSum
    parentPath = pathArr(0)
    childPathArr = pathArr(1)
    Set FSOp = CreateObject("Scripting.FileSystemObject")
    childPathSum = ""
    For Each childPath In childPathArr
	If (Len(childPath) > 0) Then
	    If (Len(childPathSum) = 0) Then
		childPathSum = childPath
	    Else
		childPathSum = childPathSum & "\" & childPath
	    End If
	    If FSOp.FolderExists(parentPath & "\" & childPathSum) Then
		If (testMode = True) Then
		    Wscript.Echo vbCrLf & "Folder exist - " & parentPath & "\" & childPathSum
		End If
	    Else
		If (testMode = True) Then
		    Wscript.Echo vbCrLf & "Create New Folder - " & parentPath & "\" & childPathSum
		Else
		    FSOp.CreateFolder(parentPath & "\" & childPathSum)
		End If
	    End If
	End If
    Next
End Function
 
Function compareFolders(foldersArr)
    Dim pathA, pathB
    Dim FSOc, getA, getB
    pathA = foldersArr(0)
    pathB = foldersArr(1)
    Set FSOc = CreateObject("Scripting.FileSystemObject")
    Set getA = FSOc.GetFolder(pathA)
    Set getB = FSOc.GetFolder(pathB)
    If ((getA.Attributes <> getB.Attributes) Or (getA.DateLastModified <> getB.DateLastModified)) Then
	compareFolders = False
    Else
	compareFolders = True
    End If
End Function
 
Function compareFiles(filesArr)
    Dim pathA, pathB
    Dim FSOf, getA, getB
    pathA = filesArr(0)
    pathB = filesArr(1)
    Set FSOf = CreateObject("Scripting.FileSystemObject")
    Set getA = FSOf.GetFile(pathA)
    Set getB = FSOf.GetFile(pathB)
    If ((getA.Size <> getB.Size) Or (getA.Attributes <> getB.Attributes) Or _
    (getA.DateLastModified <> getB.DateLastModified)) Then
	compareFiles = False
    Else
	compareFiles = True
    End If
End Function

Удаление "материнских" жестких ссылок:

dellink.vbs

Option Explicit
On Error Resume Next
Dim FSO, WshShell, testMode
Dim destPath, savePathDate
Dim countFiles
destPath = Wscript.Arguments.Named.Item("dest")
savePathDate = Wscript.Arguments.Named.Item("save")
If (Wscript.Arguments.Named.Exists("testmode")) Then
    testMode = True
Else
    testMode = False
End If
countFiles = 0
Set WshShell = WScript.CreateObject("WScript.Shell")
'WshShell.Exec "cmd /c chcp 866"
Set FSO = CreateObject("Scripting.FileSystemObject")
If (FSO.FolderExists(savePathDate)) Then
    Wscript.Echo DeleteFilesR(Array(destPath, savePathDate, ""))
End If
 
Function DeleteFilesR(pDirArr)
' Out: testMode, countFiles
    Dim destP, savPD, chldP
    Dim FSOs, FSOsf
    Dim savFolders, savFolder, savFiles, savFile, delFile
    Dim childPath, dstPathFile
    destP = pDirArr(0)
    savPD = pDirArr(1)
    chldP = pDirArr(2)
    Set FSOs = CreateObject("Scripting.FileSystemObject")
    Set FSOsf = FSOs.GetFolder(savPD)
    Set savFolders = FSOsf.SubFolders
    For Each savFolder In savFolders
	If (Len(chldP) > 0) Then 
	    childPath = chldP & "\" & savFolder.Name
	Else 
	    childPath = savFolder.Name
	End If
        DeleteFilesR(Array(destP, savPD & "\" & savFolder.Name, childPath))
    Next
    If (testMode = True) Then
	Wscript.Echo vbCrLf & "->  " & savPD
    End If
    Set savFiles = FSOsf.Files
    For Each savFile In savFiles
	if (Len(chldP) > 0) Then
	    dstPathFile = destP & "\" & chldP & "\" & savFile.Name
	Else
	    dstPathFile = destP & "\" & savFile.Name
	End If
	If (testMode = True) Then
	    Wscript.Echo "->> " & savFile.Path
	End If
	If FSOs.FileExists(dstPathFile) Then
	    If (testMode = True) Then
		Wscript.Echo "DEL " & dstPathFile
	    Else
		Set delFile = FSOs.GetFile(dstPathFile)
		delFile.Delete(True)
	    End If
	    countFiles = countFiles + 1
	End If
    Next
    If (testMode = True) Then
	DeleteFilesR = vbCrLf & "Deleted files - " & countFiles
    Else
	DeleteFilesR = countFiles
    End If
End Function

Пояснения к скриптам

  1. Для версий Windows более старых чем Vista программу Robocopy можно взять здесь. К сожалению, в этой старой версии отсутствует возможность зеркалирования меток времени для папок (... /DCOPY:T ).
    После установки программу нужно найти здесь "C:\Program Files\Windows Resource Kits\Tools" и скопировать в "C:\Windows\System32"
  2. Для версий Windows более старых чем Vista необходимо раскомментировать строку использующую fsutil hardlink create ... и закомментировать строку использующую mklink /h ... в начале файла hlinkch.vbs.
  3. При отслеживании изменений папок учитываются только папки которые были удалены (из откуда), папки у которых отличается время изменения и папки с изменившимися атрибутами. Изменения: NTFS ACL, владельца и аудита не отслеживаются.
  4. У файлов отслеживается их удаление (из откуда), время изменения, размер (но не содержимое), атрибуты. Изменения NTFS ACL, владелец и аудит - не отслеживаются. Если изменилось содержимое файла, но не изменился размер (а такое бывает), дата и атрибуты, то файл будет проигнорирован!
  5. Жесткие ссылки здесь задействованы для того, чтобы не копировать целиком "удаленные" в процессе сравнения (в .vbs), поскольку в какой-то момент (после сохранения копии в устаревшие, перед удалением из куда) они займут двойное пространство на диске (а если не копировать, а сразу перемещать, то теряются свойства файлов).
  6. Все три папки: "c:\откуда" "d:\куда" "d:\устаревшие" должны присутствовать заранее!
  7. Файл hlinkch.vbs можно запускать самостоятельно с такими параметрами:
  8. cscript hlinkch.vbs /src:"c:\откуда" /dest:"d:\куда" /save:"d:\устаревшие" /day:-2 /testmode

    • /day:-2 - установить позавчерашнюю дату в имени конечной папки, /day:0 - сегодняшняя дата
      формат имени конечной папки будет - "d:\устаревшие\yyyy_mm_dd\"
    • /testmode - имитация действий с выводом предполагаемых результатов в консоль
      отсутствие этого параметра - обычный рабочий режим с применением реальных изменений
    • В путях должны быть кавычки, в остальных параметрах их не должно быть!
    • Если папки "d:\устаревшие\yyyy_mm_dd\" нет, то она будет создана, а если она есть, то должна быть пустой
    • Если в откуда не было измененных и удаленных, то даже если там появились новые, папка "\yyy_mm_dd\" не будет создана (будет удалена после временного создания)!

    При таком самостоятельном запуске (без задействования Robocopy и "backup.bat"), в "d:\устаревшие\yyyy_mm_dd\" будут созданы только жесткие ссылки на те файлы папки куда, которые были удалены (отсутствуют) или изменены (время и размер) в папке откуда.

    Скрипт возвращает имя папки "устаревшие\yyyy_mm_dd\" либо пустую строку если эта папка была не пустая.

  9. Файл dellink.vbs можно запускать самостоятельно с такими параметрами:
  10. cscript dellink.vbs /dest:"d:\куда" /save:"d:\устаревшие" /testmode

    Этот скрипт удаляет из папки "куда" все файлы, которые присутствуют в папке "устаревшие\yyyy_mm_dd\".

    Таким образом, поскольку каждая такая "совпадающая" (по имени и относительному пути) пара имен файлов, является парой жестких ссылок на один и тот же файл, то просто разрывается жесткая ссылка (hardlink) на этот файл из папки "куда" и остается единственная ссылка на него в папке "устаревшие\yyyy_mm_dd\".

    Использование параметров (в т.ч. /testmode ) здесь аналогично предыдущему скрипту (см. выше hlinkch.vbs).

    Скрипт возвращает количество разорванных (удаленных) ссылок в папке "куда".

Интересные ключи Robocopy

robocopy c:\откуда d:\куда ...[параметры]

Некоторые полезные и малопонятные параметры (ключи):


/XO - исключить (из операций) файлы, которые (в папке откуда) старее ("older"):

- если старее окажется файл в папке куда, то он будет в ней перезаписан - все как обычно

- если файл в папке куда вдруг окажется новее, то он останется нетронутым

Т.е. если в момент сравнения файла источника (откуда) и файла назначения (куда) окажется, что в папке источника (откуда) более старый файл - этот файл просто пропускается и ничего с ним не происходит вообще.

ВАЖНО! Файл будет перезаписан (в первом случае), или не затронут (во втором случае) независимо того какой у него размер или атрибуты. Кроме того, если у обрабатываемых файлов есть посторонние внешние жесткие ссылки, то обрабатываемые файлы могут быть неверно прочитаны Robocopy - см. об этом здесь ниже в разделе о жестких ссылках.


/XN - исключить (из операций) файлы, которые (в папке откуда) новее ("newer"):

- если новее вдруг окажется файл в папке куда, то он будет в ней перезаписан

- если файл в папке куда окажется старее, то он останется нетронутым

Т.е. если в момент сравнения файла источника (откуда) и файла назначения (куда) окажется, что в папке источника (откуда) более новый файл - этот файл просто пропускается и ничего с ним не происходит вообще.

ВАЖНО! Файл будет перезаписан (в первом случае), или не затронут (во втором случае) независимо того какой у него размер или атрибуты. Кроме того, если у обрабатываемых файлов есть посторонние внешние жесткие ссылки, то обрабатываемые файлы могут быть неверно прочитаны Robocopy - см. об этом здесь ниже в разделе о жестких ссылках.


/XС - исключить (из операций) измененные ("changed") файлы:

- только одинаковое время (!) и разный размер означают, что файлы "changed"

- если файлы имеют одинаковое время и разный размер - файл в папке куда останется нетронутым (будет исключен из обработки)

- если файлы имеют разное время то даже разный размер не будет иметь значения, - файл в папке куда НЕ будет считаться "changed" и НЕ будет исключен из обработки (а будет видимо перезаписан).

- атрибуты файлов для этого ключа вообще не важны

Т.е. если в момент сравнения файла источника (откуда) и файла назначения (куда) окажется, что у файлов одинаковые метки времени, то только тогда сравнивается размер файлов, и если размер отличается, то только тогда файлы считаются "changed" и будут исключены из обработки (проигнорированы). Если метки времени разные, то независимо от их разного размера - файлы НЕ попадут в класс "changed" (и НЕ будут обрабатываться этим ключом), - т.е. НЕ будут исключены (НЕ будут проигнорированы программой), а будут перезаписаны!

ВАЖНО! Если одноименные файлы в папках откуда и куда имеют одинаковые метки времени, одинаковый размер (!), но разное содержимое (а такое тоже бывает), то Robocopy этого к сожалению не увидит - в этом случае его будет интересовать только размер файлов.

Так же надо учитывать что посторонние жесткие ссылки на обрабатываемые файлы могут сильно повлиять на достоверность результата, поскольку информация о новом размере файла может отсутствовать (даже если размер был изменен - см. ниже о жестких ссылках).


/XX - исключить (из операций) "дополнительные" ("extra") файлы и папки, - которых НЕТ в папке откуда, но они есть в папке куда.


/XL - исключить (из операций) "одинокие" ("lonely") файлы и папки, - которые ЕСТЬ в папке откуда, но отсутствуют в папке куда.


/IS - включить одинаковые ("same") файлы, - которые полностью идентичны (по времени, размеру и атрибутам)


/IT - включить "твикнутые" ("tweaked") файлы, - которые имеют одинаковый размер и метки времени, но разные атрибуты.


Полезная таблица для понимания классификации файлов в Robocopy:

File        Exists In   Exists In        Source/Dest     Source/Dest   Source/Dest
Class       Source      Destination      File Times      File Sizes    Attributes
=========== =========== ================ =============== ============= ============
Lonely      Yes         No               n/a             n/a           n/a
Tweaked     Yes         Yes              Equal           Equal         Different
Same        Yes         Yes              Equal           Equal         Equal
Changed     Yes         Yes              Equal           Different     n/a
Newer       Yes         Yes              Source > Dest   n/a           n/a
Older       Yes         Yes              Source < Dest   n/a           n/a
Extra       No          Yes              n/a             n/a           n/a
Mismatched  Yes (file)  Yes (directory)  n/a             n/a           n/a

Некоторые особенности жестких ссылок Windows

Изучая свойства жестких ссылок в Windows, обнаружил следующее:

- Изменения в правах доступа к файлу видны сразу во всех жестких ссылках на этот файл, а время изменения файла - не сразу во всех.

- Когда изменяем содержимое файла, то время изменений обновляется сразу только у той жесткой ссылки из которой эти изменения производились.

- Измененные атрибуты файла ведут себя так же как и время изменения файла (см. выше).

- Если изменить содержимое файла (например находящегося в папке "куда"), используя внешнюю жесткую ссылку на этот файл, то программа Robocopy в некоторых случаях может этого не увидеть, т.к. например время изменения файла обновилось только в той внешней ссылке, через которую он был изменен.

- Если измененный через внешнюю жесткую ссылку файл просто открыть или просто просмотреть его свойства через не затронутую ранее ссылку (например из папки "куда"), то "замороженное" время изменений и атрибуты у этой не затронутой ссылки обновятся. Если просматривая в браузере список файлов в виде таблицы добавить столбец "Владелец", то "замороженная" не затронутся ранее ссылка будет обновляться сразу (и ее время и атрибуты).

* Все вышеперечисленное касается точно только последних версий Windows. В устаревших (например XP или Vista) все может быть иначе - это нужно проверять отдельно.

ВЫВОД из вышесказанного может быть видимо следующим, - если Robocopy будет обрабатывать файл, у которого есть дополнительные используемые где-то еще жесткие ссылки, то Robocopy может не заметить, что у файла могут быть уже другие: атрибуты, время изменения, и судя по всему даже размер. И все это видимо до тех пор, пока каким-либо образом не будут запрошены права доступа к файлу, когда само чтение этих NTFS-прав автоматически обновит и сделает "читабельной" реальную дополнительную информацию (атрибуты, время и размер).

Дополнительный бэкап на другой компьютер

Выше был рассмотрен "версионный" бэкап, который сохраняет старые версии, на случай ошибки пользователя или сбоя программ.
Бывает еще бэкап предназначенный для сохранения файлов на случай поломки оборудования (дисков) или атаки вируса.
Такой бэкап лучше всего делать на отдельный физический компьютер.

Почему FTP

У бэкапа на FTP есть свои плюсы и свои минусы.
Один из очевидных плюсов - пользователь FTP не является пользователем операционной системы, как в случае сетевых папок или SSH.

Т.е. даже если вирус или злоумышленник получит полный доступ к рабочей системе и узнает все пароли, то в случае с логином и паролем FTP, возможен достаточно простой вариант, при котором это ничем не грозит.

Инициатива извне - FTP сервер на рабочем компьютере

Максимально параноидальный вариант - когда компьютер "внешнего" бекапа сам является инициатором операции резервирования - он сам по расписанию подключается к FTP серверу рабочего компьютера и копирует к себе все нужные данные.
В этом случае у внешнего компьютера могут быть закрыты все сетевые папки, и даже сам сетевой доступ, а так же все порты для входящих. Т.е. доступа к нему нет вообще, и даже нет возможности его пинговать. Кроме того из всех протоколов и служб в настройке сетевого подключения ему достаточно оставить один пункт (протокол TCP/IP v4). Тогда даже он сам не сможет иметь доступ к сетевым папкам.

Единственное что у него есть - это логин и пароль на FTP доступ к рабочему компьютеру. А поскольку пользователь FTP может быть полностью изолирован от операционной системы (чего не скажешь о пользователях сетевых папок например), то достигается максимальная защита.

Бесплатный, простой и легкий FTP-сервер для Windows можно взять например здесь - FileZilla server и установить его на рабочий компьютер. После этого нужно завести в нем пользователя и дать доступ к папке с данными (достаточно доступа только на чтение).

Для того чтобы внешний компьютер мог бэкапить к себе данные по расписанию, ему нужна программа с поддержкой командной строки. Были перепробованы разные программы (ncftp-ncftpget, aria2c, wget для windows и пр.) и единственная из них кто имеет достаточную гибкость и не имеет в Windows проблем с кириллицей в именах файлов и папок оказалась WinSCP (качать проще всего сразу версию Portable).

Скрипт WinSCP для планировщика может быть примерно таким:

full_backup.bat

@echo off
rem chcp 65001
set spath=D:\BACKUP\%date:~-4%-%date:~3,2%-%date:~0,2%
rem set spath=D:\BACKUP
"D:\Winscp_portable\winscp.com" /command "option batch continue" "open ftp://ftpUSER:ftpPASSWORD@192.168.0.2:21/" ^
"get ""/Work"" ""%spath%""" "exit"

ВАЖНО! Здесь запускается файл с расширением .com из Portable версии WinSCP. Из пути при желании можно убрать создание папки с текущей датой. IP адрес рабочего компьютера нужно подставить свой (на нем должен быть установлен FileZilla-server - см. выше). Так же надо подставить в этот скрипт свой логин и пароль FTP вместо "ftpUSER" и "ftpPASSWORD".
Папка "/Work" в контексте этой статьи, это видимо будет папка куда на рабочем компьютере (хотя конечно можно настроить FTP-сервер так чтобы он сразу давал к ней доступ как корневой для FTP).
Количество двойных кавычек ("" ... """) должно быть точно таким как указано в скрипте!

Инициатива изнутри - FTP сервер на внешнем компьютере

Чуть менее параноидальным вариантом "внешнего" бэкапа будет вариант когда рабочий компьютер не ждет когда к нему подключатся извне, а сам копирует данные на внешний FTP - по своей инициативе, и своему расписанию. В этом случае рабочий компьютер все же имеет минимальный доступ на внешний компьютер бэкапов - доступ по FTP.
В этом варианте все наоборот - на внешний компьютер необходимо установить FTP-сервер, например FileZilla-server (см. чуть выше ссылку), а на рабочий компьютер ту же программу WinSCP (ссылку на Partable см. чуть выше), и закачивать данные на внешний компьютер по расписанию так же с ее помощью.

Скрипт WinSCP для планировщика может быть примерно таким:

full_backup.bat

@echo off
chcp 1251
set spath=D:\Work
"D:\Winscp_portable\winscp.com" /command "option batch continue" "open ftp://ftpUSER:ftpPASSWORD@192.168.0.2:21/" ^
"synchronize remote -mirror -delete ""%spath%"" ""/BACKUP/Work""" "exit"

ВАЖНО! chcp 1251 - обязательно, если скрипт сохранен в Windows-кодировке (например в Блокноте). В этом случае кириллица в именах файлов и папок будет сохраняться корректно, хотя не корректно отображаться в консоли. Если необходимо и корректное отображение в консоли и в именах файлов, то необходимо строку chcp 1251 удалить, а сам файл скрипта сохранить в DOS-кодировке (например в WordPad при выборе "Сохранить как..." можно выбрать DOS-кодировку).
D:\Work - папка на локальном компьютере.
/BACKUP - существующая папка на удаленном FTP-сервере
.../Work - папка на удаленном сервере которая будет создана если ее нет.
"option batch continue" - пропускать ошибки (иначе на ошибках останавливается)
synchronize remote -mirror ... - модифицировать удаленный каталог (локальный останется нетронут)
... -delete - уничтожать в удаленном каталоге файлы которых нет в локальном
Здесь запускается файл с расширением .com из Portable версии WinSCP. IP адрес внешнего компьютера нужно подставить свой (на нем должен быть установлен FileZilla-server - см. выше). Так же надо подставить в этот скрипт свой логин и пароль FTP вместо "ftpUSER" и "ftpPASSWORD".
Количество двойных кавычек ("" ... """) должно быть точно таким как указано в скрипте!

Некоторые замечания по FileZilla Server

В FileZilla Server странно реализованы виртуальные каталоги.
Во-первых нужно указать реальный корневой каталог, - например пустую папку. Во-вторых для добавления других папок так, чтобы они были видны одним списком при доступе по FTP, нужно после добавления любой дополнительной папки (только после того как была добавлена пустая корневая) указать у этой дополнительной папки путь стандартно, а после этого навести мышь напротив строки где была добавлена эта папка и одновременно по вертикали напротив столбца "Aliases" и дважды кликнуть. После чего можно будет указать имя виртуального каталога в формате "/Папка". Сделано это к сожалению очень не интуитивно...

Последняя версия FileZilla Server не работает на XP. Можно попробовать легкий Xlight FTP Server (доступна Portable-версия), но он ощутимо медленнее, чем FileZilla Server. В настройках Xlight надо использовать только free-функции, не трогая не-free, поскольку полнофункциональный режим будет работать только в течение ограниченного триал-периода (см. подробнее здесь). Кроме того не забудьте в настройках установить его как службу.

Чтобы административный интерфейс FileZilla Server не стартовал при старте Windows (сам сервер будет запускаться как служба в любом случае, если это было указано при установке), нужно удалить ссылку на автостарт интерфейса в двух местах:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run\
HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Run\


См. так же заметку посвященную ротации файлов бэкапа: Ротация файлов бэкапа


Рекламные ссылки:
Комментариев: 2 RSS
Михаил Червоненко1
2017-09-01 в 16:58:11

По поводу CopyMik

>ВАЖНО! Есть здесь пара настораживающих моментов. Во-первых функция предварительного сохранения удаляемых и измененных файлов и папок в данный >момент присутствует только в последней альфа-версии (v2_20_9_alpha). Во-вторых несколько настораживает то, что программа хотя и бесплатная, но >полностью закрытая (исходников нет) и мало что известно об ее авторе. Если Вас например это не смущает, то можно пользоваться этой программой - >в ней именно то что нужно.

Альфа переведена в бэту v2.21b2

Исходники планируется опубликовать (просто пока не было времени их причесать (написать комментариев итп)

Автор конечно не Алла Пугачёва но например тут

https://www.xing.com/profile/Mikhail_Tchervonenko

или тут

https://habrahabr.ru/users/rusmikle/

а ещё всякие фейсбуки и одноклассники итп информацию нарыть можно при желании.

Короче лошадка я не очеь тёмная ;)

Спасибо за упоминание в статье :)

Михаил Червоненко

И Вам спасибо за комментарий. :)

На самом деле консольных программ с "третьей папкой" я больше вообще не нашел.

Ваша программа похоже уникальна в этом.

Вопросы по теме статьи (просьба - без личностей), - присутствует премодерация:
   - регистрироваться НЕ обязательно! -

Комментарий будет опубликован только после проверки

Вы можете войти под своим логином или зарегистрироваться на сайте.

(обязательно)