# @name Two-Way Synchronization with Delete...
# @command powershell.exe -ExecutionPolicy Bypass -File "%EXTENSION_PATH%" ^
# -sessionUrl "!E" -localPath "%LocalPath%" -remotePath "%RemotePath%" ^
# -listPath "%ListPath%" -refresh -pause -sessionLogPath "%SessionLogPath%"
# @description Synchronizes files on local and remote directories including file deletions ^
# by remembering a list of previous local files
# @version 2
# @homepage https://winscp.net/eng/docs/library_example_two_way_synchronize_delete
# @require WinSCP 5.18.1
# @option - -run group "Directories"
# @option LocalPath -run textbox "&Local directory:" "!\"
# @option RemotePath -run textbox "&Remote directory:" "!/"
# @option - -run group "Options"
# @option ListPath -run textbox "Re&member previous local files in:" ^
# "%LOCALAPPDATA%\WinSCP\TwoWayDelete\!N.txt"
# @option - -config group "Logging"
# @option SessionLogPath -config sessionlogfile
# @optionspage https://winscp.net/eng/docs/library_example_two_way_synchronize_delete#options
param (
# Use Generate Session URL function to obtain a value for -sessionUrl parameter.
$sessionUrl = "sftp://user:mypassword;fingerprint=ssh-rsa-xxxxxxxxxxx...@example.com/",
[Parameter(Mandatory = $True)]
$localPath,
[Parameter(Mandatory = $True)]
$remotePath,
[Parameter(Mandatory = $True)]
$listPath,
$sessionLogPath = $Null,
[Switch]
$pause,
[Switch]
$refresh
)
try
{
# Load WinSCP .NET assembly
$assemblyPath = if ($env:WINSCP_PATH) { $env:WINSCP_PATH } else { $PSScriptRoot }
Add-Type -Path (Join-Path $assemblyPath "WinSCPnet.dll")
# Setup session options from URL
$sessionOptions = New-Object WinSCP.SessionOptions
$sessionOptions.ParseUrl($sessionUrl)
$listPath = [Environment]::ExpandEnvironmentVariables($listPath)
$listDir = (Split-Path -Parent $listPath)
New-Item -ItemType directory -Path $listDir -Force | Out-Null
if (Test-Path $listPath)
{
Write-Host "Loading list of previous local files..."
[string[]]$previousFiles = Get-Content $listPath
}
else
{
Write-Host "No list of previous local files"
$previousFiles = @()
}
$needRefresh = $False
$session = New-Object WinSCP.Session
$fileTransferOptions = New-Object WinSCP.TransferOptions($Null)
$fileTransferOptions.FileMask = "*.* | Desktop.ini"
try
{
$session.SessionLogPath = $sessionLogPath
Write-Host "Connecting..."
$session.Open($sessionOptions)
Write-Host "Comparing files..."
$differences =
$session.CompareDirectories(
[WinSCP.SynchronizationMode]::Both, $localPath, $remotePath, $False, $False, "Time", $fileTransferOptions)
if ($differences.Count -eq 0)
{
Write-Host "No changes found."
}
else
{
Write-Host "Synchronizing $($differences.Count) change(s)..."
# options: new TransferOptions { FileMask = "|*.txt" });
foreach ($difference in $differences)
{
$action = $difference.Action
if ($action -eq [WinSCP.SynchronizationAction]::UploadNew)
{
if ($previousFiles -contains $difference.Local.FileName)
{
$difference.Reverse()
}
else
{
$needRefresh = $True
}
}
elseif ($action -eq [WinSCP.SynchronizationAction]::DownloadNew)
{
$localFilePath =
[WinSCP.RemotePath]::TranslateRemotePathToLocal(
$difference.Remote.FileName, $remotePath, $localPath)
if ($previousFiles -contains $localFilePath)
{
$difference.Reverse()
$needRefresh = $True
}
else
{
# noop
}
}
elseif ($action -eq [WinSCP.SynchronizationAction]::DownloadUpdate)
{
# noop
}
elseif ($action -eq [WinSCP.SynchronizationAction]::UploadUpdate)
{
$needRefresh = $True
}
else
{
throw "Unexpected difference $action"
}
Write-Host -NoNewline "$difference ..."
try
{
$transferOptions = New-Object WinSCP.TransferOptions
$transferOptions.ResumeSupport = New-Object WinSCP.TransferResumeSupport
$transferOptions.ResumeSupport.State = [WinSCP.TransferResumeSupportState]::Off
$transferOptions.AddRawSettings("ExcludeHiddenFiles", "1")
$transferOptions.FileMask = "| printru.ini, desktop.ini, ntuser.dat, lockfile.lck"
$difference.Resolve($session, $transferOptions) | Out-Null
Write-Host -NoNewline " Done."
}
finally
{
Write-Host
}
}
}
}
finally
{
# Disconnect, clean up
$session.Dispose()
}
# Refresh the remote directory in WinSCP GUI, if it was changed and -refresh switch was used
if ($refresh -and $needRefresh)
{
Write-Host "Reloading remote directory..."
& "$env:WINSCP_PATH\WinSCP.exe" "$sessionUrl" /refresh "$remotePath"
}
Write-Host "Saving current local file list..."
$localFiles =
Get-ChildItem -Recurse -Path $localPath |
Select-Object -ExpandProperty FullName
Set-Content $listPath $localFiles
Write-Host "Done."
$result = 0
}
catch
{
Write-Host "Error: $($_.Exception.Message)"
$result = 1
}
# Pause if -pause switch was used
if ($pause)
{
Write-Host "Press any key to exit..."
[System.Console]::ReadKey() | Out-Null
}
exit $result