Differences
This shows you the differences between the selected revisions of the page.
library_example_advanced_rename 2016-06-21 | library_example_advanced_rename 2023-01-11 (current) | ||
Line 7: | Line 7: | ||
* changing leading characters: ''mv new*.* old*.*'' | * changing leading characters: ''mv new*.* old*.*'' | ||
- | ===== Advanced rename with PowerShell ===== | + | ===== [[powershell]] Advanced rename with PowerShell ===== |
- | But for more advanced rename operations, you need to use favorite scripting language language, like [[library_powershell|PowerShell]], to generate a new name and use [[library|WinSCP .NET assembly]] for the actual [[library_session_movefile|rename]] (or [[guide_automation#parametrized|generate a script file]]). | + | But for more advanced rename operations, you need to use your favorite scripting language language, like [[library_powershell|PowerShell]], to generate a new name and use [[library|WinSCP .NET assembly]] for the actual [[library_session_movefile|rename]] (or [[guide_automation#parametrized|generate a script file]]). |
- | The following example prefixes all files in a specified directory with a timestamp in format ''YYYY-MM-DD-''. | + | The following example renames files using a regular expression. |
+ | |||
+ | The script is distributed in WinSCP installer as a [[extension|WinSCP extension]]. | ||
+ | |||
+ | <code powershell - BatchRename.ps1> | ||
+ | # @name Batch &Rename... | ||
+ | # @command powershell.exe -ExecutionPolicy Bypass -File "%EXTENSION_PATH%" ^ | ||
+ | # -sessionUrl "!E" -remotePath "!/" -pattern "%Pattern%" ^ | ||
+ | # -replacement "%Replacement%" -refresh -pause -sessionLogPath "%SessionLogPath%" ^ | ||
+ | # %PreviewMode% !& | ||
+ | # @description Renames remote files using a regular expression | ||
+ | # @flag RemoteFiles | ||
+ | # @version 7 | ||
+ | # @homepage ~~SELF~~ | ||
+ | # @require WinSCP 5.19 | ||
+ | # @option - -run group "Rename" | ||
+ | # @option Pattern -run textbox "Replace file name part matching this pattern:" | ||
+ | # @option Replacement -run textbox "with:" | ||
+ | # @option - -run -config group "Options" | ||
+ | # @option PreviewMode -run -config checkbox "&Preview changes" "-previewMode" ^ | ||
+ | # "-previewMode" | ||
+ | # @option - -config group "Logging" | ||
+ | # @option SessionLogPath -config sessionlogfile | ||
+ | # @optionspage ~~SELF~~#options | ||
- | <code powershell> | ||
param ( | param ( | ||
- | $sessionUrl = "sftp://user:mypassword;fingerprint=ssh-rsa-xx-xx-xx@example.com/", | + | # Use Generate Session URL function to obtain a value for -sessionUrl parameter. |
- | $remotePath = "/path" | + | ····$sessionUrl = "sftp://user:mypassword;fingerprint=ssh-rsa-xxxxxxxxxxx...@example.com/", |
+ | [Parameter(Mandatory = $True)] | ||
+ | $remotePath, | ||
+ | [Parameter(Mandatory = $True)] | ||
+ | ····$pattern, | ||
+ | $replacement, | ||
+ | [Switch] | ||
+ | $pause, | ||
+ | [Switch] | ||
+ | $refresh, | ||
+ | $sessionLogPath = $Null, | ||
+ | [Switch] | ||
+ | $previewMode, | ||
+ | [Parameter(Mandatory = $True, ValueFromRemainingArguments = $True, Position = 0)] | ||
+ | $files | ||
) | ) | ||
- | · | + | |
try | try | ||
{ | { | ||
- | # Load WinSCP .NET assembly | + | if ($previewMode) |
- | Add-Type -Path "WinSCPnet.dll" | + | |
- | + | ||
- | # Setup session options | + | |
- | $sessionOptions = New-Object WinSCP.SessionOptions | + | |
- | $sessionOptions.ParseUrl($sessionUrl) | + | |
- | + | ||
- | try | + | |
{ | { | ||
- | # Connect | + | $anyChange = $False |
- | Write-Host "Connecting..." | + | foreach ($file in $files) |
- | ·······$session = New-Object WinSCP.Session | + | ········{ |
- | ·······$session.Open($sessionOptions) | + | ···········$newName = $file -replace $pattern, $replacement |
- | + | ············if ($newName -eq $file) | |
- | ········# Retrieve file list | + | ···········{ |
- | ·······Write-Host "Listing..." | + | ···············Write-Host "$file not changed" |
- | ·······$files = $session.EnumerateRemoteFiles($remotePath, "*", [WinSCP.EnumerationOptions]::None) | + | ···········} |
+ | ···········else | ||
+ | ············{ | ||
+ | ················Write-Host "$file => $newName" | ||
+ | ···············$anyChange = $True | ||
+ | ············} | ||
+ | ········} | ||
- | Write-Host "Renaming..." | + | Write-Host |
- | $timestampPrefix = $(Get-Date -f "yyyy-MM-dd") + "-" | + | if (!$anyChange) |
- | + | ||
- | foreach ($fileInfo in $files) | + | |
{ | { | ||
- | $oldName = $fileInfo.Name | + | Write-Host "No change to be made" |
- | $oldPath = $fileInfo.FullName | + | $continue = $False |
- | ··········· | + | ········} |
- | $newName = ($timestampPrefix + $oldName) | + | else |
- | $newPath = $session.CombinePaths($remotePath, $newName) | + | { |
- | + | Write-Host -NoNewline "Continue? y/N " | |
- | Write-Host ("{0} => {1}" -f $oldPath, $newPath) | + | $key = [System.Console]::ReadKey() |
- | $session.MoveFile($oldPath, $newPath) | + | Write-Host |
+ | Write-Host | ||
+ | ···········$continue = ($key.KeyChar -eq "y") | ||
+ | ···········if (!$continue) | ||
+ | { | ||
+ | ················$pause = $False | ||
+ | } | ||
} | } | ||
- | |||
- | Write-Host "Done" | ||
} | } | ||
- | finally | + | else |
{ | { | ||
- | # Disconnect, clean up | + | $continue = $True |
- | ········$session.Dispose() | + | |
} | } | ||
- | + | ||
- | exit 0 | + | if (!$continue) |
+ | { | ||
+ | $result = 1 | ||
+ | } | ||
+ | else | ||
+ | { | ||
+ | # 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 | ||
+ | $sessionOptions = New-Object WinSCP.SessionOptions | ||
+ | $sessionOptions.ParseUrl($sessionUrl) | ||
+ | |||
+ | $session = New-Object WinSCP.Session | ||
+ | |||
+ | try | ||
+ | { | ||
+ | $session.SessionLogPath = $sessionLogPath | ||
+ | |||
+ | Write-Host "Connecting..." | ||
+ | $session.Open($sessionOptions) | ||
+ | |||
+ | Write-Host "Renaming..." | ||
+ | foreach ($file in $files) | ||
+ | { | ||
+ | $newName = $file -replace $pattern, $replacement | ||
+ | if ($newName -eq $file) | ||
+ | { | ||
+ | Write-Host "$file not changed" | ||
+ | } | ||
+ | else | ||
+ | { | ||
+ | Write-Host "$file => $newName" | ||
+ | $fileMask = [WinSCP.RemotePath]::EscapeFileMask($file) | ||
+ | $sourcePath = [WinSCP.RemotePath]::Combine($remotePath, $fileMask) | ||
+ | $operationMask = [WinSCP.RemotePath]::EscapeOperationMask($newName) | ||
+ | $targetPath = [WinSCP.RemotePath]::Combine($remotePath, $operationMask) | ||
+ | $session.MoveFile($sourcePath, $targetPath) | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | finally | ||
+ | { | ||
+ | # Disconnect, clean up | ||
+ | $session.Dispose() | ||
+ | } | ||
+ | |||
+ | if ($refresh) | ||
+ | { | ||
+ | & "$env:WINSCP_PATH\WinSCP.exe" "$sessionUrl" /refresh "$remotePath" | ||
+ | } | ||
+ | } | ||
+ | |||
+ | $result = 0 | ||
} | } | ||
- | catch [Exception] | + | catch |
{ | { | ||
- | Write-Host ("Error: {0}" -f $_.Exception.Message) | + | Write-Host "Error: $($_.Exception.Message)" |
- | exit 1 | + | $result = 1 |
} | } | ||
+ | |||
+ | if ($pause) | ||
+ | { | ||
+ | Write-Host "Press any key to exit..." | ||
+ | [System.Console]::ReadKey() | Out-Null | ||
+ | } | ||
+ | |||
+ | exit $result | ||
</code> | </code> | ||
+ | |||
+ | ==== [[options]] Options ==== | ||
+ | |||
+ | &screenshotpict(extension_batch_rename) | ||
+ | |||
+ | In the //"Replace file name part matching this pattern"// box, enter a [[wp>Regular_expression|regular expression]] pattern to look for in file name(s). | ||
+ | |||
+ | In the //with// box, enter a replacement string to replace the file name part that matches the regular expression with. The replacement string can use ''$x'' syntax to refer to the ''x''-th group (subexpression in parentheses) captured by the pattern. | ||
+ | |||
+ | On the image, you can see a pattern (''(\d{4})-(\d{2})-(\d{2})'') and a replacement string (''$2-$3-$1'') that change date stamp in file names from the ''yyyy-mm-dd'' format to the ''mm-dd-yyyy'' format by changing an order of captured groups. | ||
+ | |||
+ | Check the //Preview changes// checkbox to see and confirm the changes in file names before actually renaming the files. | ||
+ | |||
+ | 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. | ||
+ |