Differences

This shows you the differences between the selected revisions of the page.

library_example_keep_local_directory_up_to_date 2018-03-04 library_example_keep_local_directory_up_to_date 2023-06-21 (current)
Line 12: Line 12:
<code> <code>
-powershell.exe -File KeepLocalUpToDate.ps1 -remotePath "/remote/path" +powershell.exe -File KeepLocalUpToDate.ps1 -sessionUrl "sftp://username:password;fingerprint=ssh-rsa-xxxxxxxxxxx...@example.com/" -remotePath "/remote/path"·-localPath "C:\local\path" [-delete]
--localPath "C:\local\path" [-delete]+
</code> </code>
-//If you just want to scan for changes, but not download them, see [[library_example_watch_for_changes|Watching for changes in SFTP/FTP server]].//+//If you just want to scan for changes, but not download them, see [[library_example_watch_for_changes|*]].//
<code powershell - KeepLocalUpToDate.ps1> <code powershell - KeepLocalUpToDate.ps1>
# @name        &Keep Local Directory up to Date... # @name        &Keep Local Directory up to Date...
-# @command      powershell.exe -ExecutionPolicy Bypass -File "%EXTENSION_PATH%" -sessionUrl "!S" -localPath "%LocalPath%" -remotePath "%RemotePath%" %Delete% %Beep% %ContinueOnError% -interval "%Interval%" -pause -sessionLogPath "%SessionLogPath%" +# @command      powershell.exe -ExecutionPolicy Bypass -File "%EXTENSION_PATH%"
-# @description  Periodically scans for changes in a remote directory and reflects them on a local directory +#                  -sessionUrl "!E" -localPath "%LocalPath%" -remotePath "%RemotePath%"
-# @version      4+#                  %Delete% %Beep% %ContinueOnError% -interval "%Interval%" ^ 
 +#                  -fileMask "%FileMask%" -pause -sessionLogPath "%SessionLogPath%" 
 +# @description  Periodically scans for changes in a remote directory and
 +#                  reflects them on a local directory 
 +# @version      11
# @homepage    ~~SELF~~ # @homepage    ~~SELF~~
-# @require      WinSCP 5.9.2+# @require      WinSCP 5.16
# @option      - -run group "Directories" # @option      - -run group "Directories"
# @option        RemotePath -run textbox "&Watch for changes in the remote directory:" "!/" # @option        RemotePath -run textbox "&Watch for changes in the remote directory:" "!/"
-# @option        LocalPath -run textbox "... &and automatically reflect them on the local directory:" "!\"+# @option        LocalPath -run textbox
 +#                    "... &and automatically reflect them on the local directory:" "!\"
# @option      - -config -run group "Options" # @option      - -config -run group "Options"
# @option        Delete -config -run checkbox "&Delete files" "" -delete # @option        Delete -config -run checkbox "&Delete files" "" -delete
Line 33: Line 37:
# @option        ContinueOnError -config -run checkbox "Continue on &error" "" -continueOnError # @option        ContinueOnError -config -run checkbox "Continue on &error" "" -continueOnError
# @option        Interval -config -run textbox "&Interval (in seconds):" "30" # @option        Interval -config -run textbox "&Interval (in seconds):" "30"
 +# @option        FileMask -config -run textbox "File &mask:" ""
# @option      - -config group "Logging" # @option      - -config group "Logging"
# @option        SessionLogPath -config sessionlogfile # @option        SessionLogPath -config sessionlogfile
-# @optionspage  ~~SELF~~#options+# @optionspage 
 +#    ~~SELF~~#options
param ( param (
    # Use Generate Session URL function to obtain a value for -sessionUrl parameter.     # Use Generate Session URL function to obtain a value for -sessionUrl parameter.
-    $sessionUrl = "sftp://user:mypassword;fingerprint=ssh-rsa-xx-xx-xx@example.com/",+    $sessionUrl = "sftp://user:mypassword;fingerprint=ssh-rsa-xxxxxxxxxxx...@example.com/",
    [Parameter(Mandatory = $True)]     [Parameter(Mandatory = $True)]
    $localPath,     $localPath,
Line 52: Line 58:
    $sessionLogPath = $Null,     $sessionLogPath = $Null,
    $interval = 30,     $interval = 30,
 +    $fileMask = $Null,
    [Switch]     [Switch]
    $pause     $pause
) )
 +
 +function Beep()
 +{
 +    if ($beep)
 +    {
 +        [System.Console]::Beep()
 +    }
 +}
 +
 +function HandleException ($e)
 +{
 +    if ($continueOnError)
 +    {
 +        Write-Host -ForegroundColor Red $_.Exception.Message
 +        Beep
 +    }
 +    else
 +    {
 +        throw $e
 +    }
 +}
 +
 +function SetConsoleTitle ($status)
 +{
 +    if ($sessionOptions)
 +    {
 +        $status = "$sessionOptions - $status"
 +    }
 +    $Host.UI.RawUI.WindowTitle = $status
 +}
try try
Line 65: Line 102:
    $sessionOptions = New-Object WinSCP.SessionOptions     $sessionOptions = New-Object WinSCP.SessionOptions
    $sessionOptions.ParseUrl($sessionUrl)     $sessionOptions.ParseUrl($sessionUrl)
 +
 +    $transferOptions = New-Object WinSCP.TransferOptions -Property @{ FileMask = $fileMask };
    $session = New-Object WinSCP.Session     $session = New-Object WinSCP.Session
       
-    # Optimization 
-    # (do not waste time enumerating files, if you do not need to scan for deleted files) 
-    if ($delete)  
-    { 
-        $localFiles = Get-ChildItem -Recurse -Path $localPath 
-    } 
- 
    try     try
    {     {
Line 80: Line 112:
        Write-Host "Connecting..."         Write-Host "Connecting..."
 +        SetConsoleTitle "Connecting"
        $session.Open($sessionOptions)         $session.Open($sessionOptions)
        while ($True)         while ($True)
        {         {
-            Write-Host "Synchronizing changes..." +            Write-Host -NoNewline "Looking for changes..." 
-            $result = +            SetConsoleTitle &quot;Looking for changes&quot; 
-················$session.SynchronizeDirectories( +            try
-                   [WinSCP.SynchronizationMode]::Local, $localPath, $remotePath, $delete) +
- +
-            $changed = $False +
- +
-            if (!$result.IsSuccess)+
            {             {
-              if ($continueOnError) +················$differences = 
-             { + ···················$session.CompareDirectories( 
- ···············Write-Host &quot;Error: $($result.Failures[0].Message)&quot+ ·······················[WinSCP.SynchronizationMode]::Local, $localPath, $remotePath, $delete, 
- ···············$changed = $True + ·······················$False, [WinSCP.SynchronizationCriteria]::Time, $transferOptions)
-              } +
- ·············else +
-··············+
- ···············$result.Check() +
-··············} +
- ···········}+
-············# Print updated files +················Write-Host 
- ···········foreach ($download in $result.Downloads+ ···············if ($differences.Count -eq 0
- ···········+ ···············
- ···············Write-Host "$($download.Destination) <= $($download.FileName)+ ···················Write-Host "No changes found."··· 
-                $changed = $True +                } 
-············} +                else
- +
-            if ($delete) +
-            { +
-                # scan for removed local files (the $result does not include them) +
-                $localFiles2 = Get-ChildItem -Recurse -Path $localPath +
- +
-                if ($localFiles)+
                {                 {
-                    $changes = +                    Write-Host &quot;Synchronizing $($differences.Count) change(s)..." 
-                        Compare-Object -DifferenceObject $localFiles2 ` +                    SetConsoleTitle &quot;Synchronizing changes
-                            -ReferenceObject $localFiles + ···················Beep
-                 +
-····················$removedFiles = +
-························$changes | +
-························Where-Object -FilterScript { $_.SideIndicator -eq "&lt;=" } | +
- ·······················Select-Object -ExpandProperty InputObject+
-                    # Print removed local files +                    foreach ($difference in $differences)
-····················foreach ($removedFile in $removedFiles)+
                    {                     {
-                        Write-Host "$removedFile deleted+                        $action = $difference.Action 
-                        $changed = $True +                        if ($action -eq [WinSCP.SynchronizationAction]::DownloadNew) 
- ···················+                        { 
- ···············}+                            $message = "Downloading new $($difference.Remote.FileName)..." 
 +                       } 
 +                        elseif ($action -eq [WinSCP.SynchronizationAction]::DownloadUpdate) 
 +                        { 
 +                            $message = "Downloading updated $($difference.Remote.FileName)...
 +                       
 +                        elseif ($action -eq [WinSCP.SynchronizationAction]::DeleteLocal) 
 +                        { 
 +                            $message = "Deleting $($difference.Local.FileName)..." 
 + ·······················
 +                       else 
 +                        { 
 +                            throw "Unexpected difference $action" 
 +························}
-················$localFiles = $localFiles2 +························Write-Host -NoNewline $message
-            }+
-············if ($changed+························try 
- ···········{ +                       { 
- ···············if ($beep) +                            $difference.Resolve($session, $transferOptions) | Out-Null 
- ···············+ ···························Write-Host "; Done.&quot; 
- ···················[System.Console]::Beep()+                        } 
 +                        catch 
 + ·······················
 + ···························Write-Host 
 +····························HandleException $_ 
 +························} 
 +····················}
                }                 }
            }             }
-            else+            catch
            {             {
-                Write-Host &quot;No change.";+                Write-Host 
 + ···············HandleException $_
            }             }
-············ + 
-            Write-Host "Waiting for $interval seconds, press Ctrl+C to abort..."+            SetConsoleTitle "Waiting"
            $wait = [int]$interval             $wait = [int]$interval
            # Wait for 1 second in a loop, to make the waiting breakable             # Wait for 1 second in a loop, to make the waiting breakable
            while ($wait -gt 0)             while ($wait -gt 0)
            {             {
 +                Write-Host -NoNewLine "`rWaiting for $wait seconds, press Ctrl+C to abort... "
                Start-Sleep -Seconds 1                 Start-Sleep -Seconds 1
                $wait--                 $wait--
            }             }
 +            Write-Host
            Write-Host             Write-Host
        }         }
Line 164: Line 194:
    finally     finally
    {     {
 +        Write-Host # to break after "Waiting..." status
        Write-Host "Disconnecting..."         Write-Host "Disconnecting..."
        # Disconnect, clean up         # Disconnect, clean up
Line 171: Line 202:
catch catch
{ {
-    Write-Host &quot;Error: $($_.Exception.Message)"+    $continueOnError = $True 
 +····HandleException $_ 
 +····SetConsoleTitle &quot;Error"
} }
Line 199: Line 232:
In the //Interval// box, specify an interval between the checks for changes. In the //Interval// box, specify an interval between the checks for changes.
-In the //Session log file// you can specify a path to a [[logging|session log file]]. The option is available on the [[ui_pref_commands|Preferences dialog]] only. +In the //File mask// box, you can specify [[file_mask|file mask]] to select/deselect files (or file types) and directories for the synchronization. 
- + 
 +In the //Session log file//, you can specify a path to a [[logging|session log file]]. The option is available on the [[ui_pref_commands|Preferences dialog]] only. 
 + 
 +In the //Keyboard shortcut//, you can specify a [[custom_key_shortcuts|keyboard shortcut]] for the extension. The option is available on the [[ui_pref_commands|Preferences dialog]] only.
===== [[scripting]] Scripting ===== ===== [[scripting]] Scripting =====
Line 208: Line 244:
To make the synchronization run periodically, you can either [[guide_schedule|schedule the script]] or run the script in a loop. The latter may be more convenient, as it allows you to see all runs in a single console window, so that you can review the updates easily. To make the synchronization run periodically, you can either [[guide_schedule|schedule the script]] or run the script in a loop. The latter may be more convenient, as it allows you to see all runs in a single console window, so that you can review the updates easily.
-To run the script in the loop, you can use following batch file:((Note that the ''[[https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/timeout_1|timeout]]'' command is available since Windows 7 only. For alternatives in older versions of Windows, refer to [[https://stackoverflow.com/q/1672338/850848|How to sleep for 5 seconds in Windows's Command Prompt?]] article.))+To run the script in the loop, you can use following batch file:((Note that the ''[[https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/timeout|timeout]]'' command is available since Windows 7 only. For alternatives in older versions of Windows, refer to [[https://stackoverflow.com/q/1672338/850848|How to sleep for 5 seconds in Windows's Command Prompt?]] article.))
<code batch> <code batch>
@echo off @echo off

Last modified: by martin