Reputation: 22497
Using WinSCP, is there any way that I could sync both ways (between SFTP and local directory) including deletions?
The scenario would be the following: If a file is present on either side and not on the other, it should be created (synchronize both
would do that just fine). If a file was deleted on either side, it should be deleted on the other side.
I suppose I would need some kind of log to be able to track when a file was deleted, on one or the other side so the change can be reflected on the other side.
I don't think that WinSCP has any command that does that, if I am right. Has anyone done that already?
Upvotes: 1
Views: 1266
Reputation: 202504
WinSCP cannot do that. Its synchronization is stateless. For what you want to do, you would have to indeed remember the previous state of the directories. To be able to decide, if an unmatched file was added on one side or removed on the other.
This is hardly doable with plain WinSCP scripting and a batch file. But with WinSCP .NET assembly from a PowerShell script, it is not that difficult. You can use the Session.CompareDirectories
method with the "both" mode. For the UploadNew
and DownloadNew
differences, customize them to removals, when appropriate (using a cached file list for the decision).
There is WinSCP extension Two-Way Synchronization with Delete with SFTP/FTP server:
try
{
Add-Type -Path "WinSCPnet.dll"
$sessionOptions = New-Object WinSCP.SessionOptions -Property @{
Protocol = [WinSCP.Protocol]::Sftp
HostName = "example.com"
UserName = "user"
Password = "password"
SshHostKeyFingerprint = "ssh-rsa 2048 xxxxxxxxxxx...="
}
$localPath = "C:\local\path"
$remotePath = "/remote/path"
$previousFilesFile = "previous.txt"
if (Test-Path $previousFilesFile)
{
Write-Host "Loading list of previous local files..."
[string[]]$previousFiles = Get-Content $previousFilesFile
}
else
{
Write-Host "No list of previous local files"
$previousFiles = @()
}
$session = New-Object WinSCP.Session
try
{
# Connect
Write-Host "Connecting..."
$session.Open($sessionOptions)
$differences =
$session.CompareDirectories(
[WinSCP.SynchronizationMode]::Both, $localPath, $remotePath,
$False)
if ($differences.Count -eq 0)
{
Write-Host "No changes found."
}
else
{
Write-Host "Synchronizing $($differences.Count) change(s)..."
foreach ($difference in $differences)
{
$action = $difference.Action
if ($action -eq [WinSCP.SynchronizationAction]::UploadNew)
{
if ($previousFiles -contains $difference.Local.FileName)
{
Write-Host "Removing local file $path..."
Remove-Item -Recurse $path
}
else
{
$path = $difference.Local.FileName
Write-Host "Uploading new $path..."
$difference.Resolve($session) | Out-Null
}
}
elseif ($action -eq [WinSCP.SynchronizationAction]::DownloadNew)
{
$localFilePath =
[WinSCP.RemotePath]::TranslateRemotePathToLocal(
$difference.Remote.FileName, $remotePath, $localPath)
if ($previousFiles -contains $localFilePath)
{
$path = $difference.Remote.FileName
Write-Host "Removing remote file $path..."
$path = [WinSCP.RemotePath]::EscapeFileMask($path)
$session.RemoveFiles($path).Check()
}
else
{
$path = $difference.Remote.FileName
Write-Host "Downloading new $path..."
$difference.Resolve($session) | Out-Null
}
}
elseif ($action -eq [WinSCP.SynchronizationAction]::DownloadUpdate)
{
$path = $difference.Remote.FileName
Write-Host "Downloading updated $path..."
$difference.Resolve($session) | Out-Null
}
elseif ($action -eq [WinSCP.SynchronizationAction]::UploadUpdate)
{
$path = $difference.Local.FileName
Write-Host "Uploading updated $path..."
$difference.Resolve($session) | Out-Null
}
else
{
throw "Unexpected difference $action"
}
}
}
}
finally
{
# Disconnect, clean up
$session.Dispose()
}
Write-Host "Saving current local file list..."
$localFiles =
Get-ChildItem -Recurse -Path $localPath |
Select-Object -ExpandProperty FullName
Set-Content $previousFilesFile $localFiles
Write-Host "Done."
exit 0
}
catch
{
Write-Host "Error: $($_.Exception.Message)"
exit 1
}
Upvotes: 3