Differences

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

library_example_keep_local_directory_up_to_date 2016-10-12 library_example_keep_local_directory_up_to_date 2023-06-21 (current)
Line 6: Line 6:
The script is distributed in WinSCP installer as a [[extension|WinSCP extension]]. The script is distributed in WinSCP installer as a [[extension|WinSCP extension]].
 +
 +~~AD~~
To run the script manually, use: To run the script manually, use:
<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      2+#                  %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
# @option        Beep -config -run checkbox "&Beep on change" "" -beep # @option        Beep -config -run checkbox "&Beep on change" "" -beep
-# @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 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 43: Line 51:
    $remotePath,     $remotePath,
    [Switch]     [Switch]
-    $delete = $False,+    $delete,
    [Switch]     [Switch]
-    $beep = $False,+    $beep,
    [Switch]     [Switch]
-    $continueOnError = $False,+    $continueOnError,
    $sessionLogPath = $Null,     $sessionLogPath = $Null,
    $interval = 30,     $interval = 30,
 +    $fileMask = $Null,
    [Switch]     [Switch]
-    $pause = $False+    $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 63: 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 78: 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 = $session.SynchronizeDirectories([WinSCP.SynchronizationMode]::Local, $localPath, $remotePath, $delete) +            SetConsoleTitle &quot;Looking for changes&quot; 
- +            try
-            $changed = $False +
- +
-            if (!$result.IsSuccess)+
            {             {
-              if ($continueOnError) +················$differences = 
- ·············{ + ···················$session.CompareDirectories( 
-················Write-Host ("Error: {0}" -f $result.Failures[0].Message) + ·······················[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 ("{0} <= {1}" -f $download.Destination, $download.FileName) +
-                $changed = $True +
-            } +
- +
-············if ($delete) +
-            { +
-                # scan for removed local files (the $result does not include them) +
-                $localFiles2 = Get-ChildItem -Recurse -Path $localPath +
-                $changes = Compare-Object -DifferenceObject $localFiles2 -ReferenceObject $localFiles +
-                 +
-                $removedFiles = +
-                    $changes | +
-                    Where-Object -FilterScript { $_.SideIndicator -eq "<=" } | +
-                    Select-Object -ExpandProperty InputObject +
- +
-                # Print removed local files +
-                foreach ($removedFile in $removedFiles)+
                {                 {
-                    Write-Host ("{0} deleted" -f $removedFile) +                    Write-Host "No changes found." ··
-                    $changed = $True+
                }                 }
 +                else
 +                {
 +                    Write-Host "Synchronizing $($differences.Count) change(s)..."
 +                    SetConsoleTitle "Synchronizing changes"
 +                    Beep
-················$localFiles = $localFiles2 +                    foreach ($difference in $differences) 
- ···········}+                    { 
 +                        $action = $difference.Action 
 +                        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" 
 + ·······················}
-············if ($changed) +························Write-Host -NoNewline $message 
- ···········{ + 
- ···············if ($beep+ ·······················try 
- ···············+ ·······················
- ···················[System.Console]::Beep()+                           $difference.Resolve($session, $transferOptions) | Out-Null 
 +                            Write-Host " Done." 
 +                        } 
 +                        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 154: Line 194:
    finally     finally
    {     {
 +        Write-Host # to break after "Waiting..." status
        Write-Host "Disconnecting..."         Write-Host "Disconnecting..."
        # Disconnect, clean up         # Disconnect, clean up
Line 159: Line 200:
    }     }
} }
-catch [Exception]+catch
{ {
-    Write-Host (&quot;Error: {0}" -f $_.Exception.Message)+    $continueOnError = $True 
 +····HandleException $_ 
 + ···SetConsoleTitle "Error&quot;
} }
Line 175: Line 218:
</code> </code>
-===== Options =====+===== [[options]] Options ===== 
 + 
 +&screenshotpict(extension_keep_local_directory_up_to_date)
Enter root directories for the synchronization into the two directory boxes. By default the current working directories will be used. The directories can be specified, when executing the extension only. Enter root directories for the synchronization into the two directory boxes. By default the current working directories will be used. The directories can be specified, when executing the extension only.
Line 187: 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. 
-  + 
-===== Scripting =====+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 =====
Or you can simply use the ''[[scriptcommand_synchronize|synchronize]]'' scripting command. Or you can simply use the ''[[scriptcommand_synchronize|synchronize]]'' scripting command.
Line 195: 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://technet.microsoft.com/en-us/library/cc754891.aspx|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