Differences
This shows you the differences between the selected revisions of the page.
library_example_recursive_search_text 2016-05-31 | library_example_recursive_search_text 2025-04-10 (current) | ||
Line 1: | Line 1: | ||
====== Search recursively for text in remote directory / Grep files over SFTP/FTP protocol ====== | ====== Search recursively for text in remote directory / Grep files over SFTP/FTP protocol ====== | ||
- | The following example uses [[library|WinSCP .NET assembly]] from a [[library_powershell|PowerShell]] script. If you have another preferred language, you can easily translate it. | + | The following script uses [[library|WinSCP .NET assembly]] from a [[library_powershell|PowerShell]] script. If you have another preferred language, you can easily translate it. |
- | //In the latest beta version//, the example is distributed in WinSCP installer as a [[extension|WinSCP extension]]. &beta | + | The script is distributed in WinSCP installer as a [[extension|WinSCP extension]]. |
- | You can run the script (e.g. ''SearchText.ps1'') from WinSCP GUI using [[guide_custom_commands_automation|local custom command]]: | + | To run the script manually use: |
<code> | <code> | ||
- | powershell.exe -File SearchText.ps1 -sessionUrl "!S" -path "!/" -text "!?Text:?!" -pause | + | powershell.exe -File SearchText.ps1 -sessionUrl "sftp://username:password;fingerprint=ssh-rsa-xxxxxxxxxxx...@example.com/" -path "/path" -text "text" |
</code> | </code> | ||
- | See also [[library_example_listing_files_matching_wildcard|Listing files matching wildcard]]. | + | See also [[library_example_listing_files_matching_wildcard|*]]. |
You can alter the script for other tasks, instead of grepping the matching files. You can for example [[library_session_removefiles|remove]] or [[library_session_getfiles|download]] the matching files. Just modify the action in the ''Action on match'' block accordingly. | You can alter the script for other tasks, instead of grepping the matching files. You can for example [[library_session_removefiles|remove]] or [[library_session_getfiles|download]] the matching files. Just modify the action in the ''Action on match'' block accordingly. | ||
- | |||
- | ===== In the Beta Version ===== | ||
- | In the latest beta version, you can simplify the implementation by using ''[[library_session_enumerateremotefiles|Session.EnumerateRemoteFiles]]''. &beta | ||
<code powershell - SearchText.ps1> | <code powershell - SearchText.ps1> | ||
# @name &Search for Text... | # @name &Search for Text... | ||
- | # @command powershell.exe -ExecutionPolicy Bypass -File "%EXTENSION_PATH%" -sessionUrl "!S" -path "!/" -text "%Text%" -wildcard "%Wildcard%" -pause -sessionLogPath "%SessionLogPath%" | + | # @command powershell.exe -ExecutionPolicy Bypass -File "%EXTENSION_PATH%" ^ |
+ | # -sessionUrl "!E" -path "!/" -text "%Text%" -wildcard "%Wildcard%" ^ | ||
+ | # -pause -sessionLogPath "%SessionLogPath%" | ||
# @description Searches recursively for a text in the current remote directory | # @description Searches recursively for a text in the current remote directory | ||
- | # @version 2 | + | # @version 8 |
# @homepage ~~SELF~~ | # @homepage ~~SELF~~ | ||
- | # @require WinSCP 5.8.4 | + | # @require WinSCP 5.16 |
# @option Text -run textbox "Text:" | # @option Text -run textbox "Text:" | ||
# @option Wildcard -run textbox "File mask:" "*.*" | # @option Wildcard -run textbox "File mask:" "*.*" | ||
- | # @option SessionLogPath -config file "&Session log file:" | + | # @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)] | + | [Parameter(Mandatory = $True)] |
$path, | $path, | ||
- | [Parameter(Mandatory)] | + | [Parameter(Mandatory = $True)] |
$text, | $text, | ||
$wildcard = "*.*", | $wildcard = "*.*", | ||
$sessionLogPath = $Null, | $sessionLogPath = $Null, | ||
[Switch] | [Switch] | ||
- | $pause = $False | + | $pause |
) | ) | ||
try | try | ||
{ | { | ||
+ | if (!$text) | ||
+ | { | ||
+ | throw "No Text was specified." | ||
+ | } | ||
+ | |||
# Load WinSCP .NET assembly | # Load WinSCP .NET assembly | ||
$assemblyPath = if ($env:WINSCP_PATH) { $env:WINSCP_PATH } else { $PSScriptRoot } | $assemblyPath = if ($env:WINSCP_PATH) { $env:WINSCP_PATH } else { $PSScriptRoot } | ||
Line 74: | Line 78: | ||
# matching files, instead of grepping their contents | # matching files, instead of grepping their contents | ||
- | Write-Host ("File {0} matches mask, searching contents..." -f $fileInfo.FullName) | + | if ($fileInfo.FileType -eq "L") |
- | $tempPath = (Join-Path $env:temp $fileInfo.Name) | + | |
- | # Download file to temporary directory | + | |
- | $transferResult = $session.GetFiles($session.EscapeFileMask($fileInfo.FullName), $tempPath) | + | |
- | # Did the download succeeded? | + | |
- | if (!$transferResult.IsSuccess) | + | |
{ | { | ||
- | # Print error (but continue with other files) | + | Write-Host "Skipping symlink $($fileInfo.FullName)..." |
- | ···············Write-Host $transferResult.Failures[0].Message | + | |
} | } | ||
else | else | ||
{ | { | ||
- | # Search and print lines containing "text". | + | Write-Host "File $($fileInfo.FullName) matches mask, searching contents..." |
- | # Use -Pattern instead of -SimpleMatch for regex search | + | $tempPath = (Join-Path $env:temp $fileInfo.Name) |
- | $matchInfo = Select-String -Path $tempPath -SimpleMatch $text | + | |
- | # Print the results | + | |
- | foreach ($match in $matchInfo) | + | |
- | { | + | |
- | ····················Write-Host ($fileInfo.FullName + ":" + $match.LineNumber + ":" + $match.Line) | + | |
- | } | + | |
- | # Delete temporary local copy | + | |
- | Remove-Item $tempPath | + | |
- | } | + | |
- | } | + | |
- | } | + | |
- | finally | + | |
- | { | + | |
- | # Disconnect, clean up | + | |
- | $session.Dispose() | + | |
- | } | + | |
- | + | ||
- | $result = 0 | + | |
- | } | + | |
- | catch [Exception] | + | |
- | { | + | |
- | Write-Host ("Error: {0}" -f $_.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 | + | |
- | </code> | + | |
- | + | ||
- | ==== Options ==== | + | |
- | + | ||
- | In the //Text// box, specify the text to look for. | + | |
- | + | ||
- | In the //File mask// box, specify a [[file_mask|file mask]] to select files. | + | |
- | + | ||
- | In the //Session log file// you can specify a path to a [[logging|session log file]]. | + | |
- | + | ||
- | ===== In the Stable Version ===== | + | |
- | + | ||
- | <code powershell> | + | |
- | param ( | + | |
- | # Use Generate URL function to obtain a value for -sessionUrl parameter. | + | |
- | ····$sessionUrl = "sftp://user:mypassword;fingerprint=ssh-rsa-xx-xx-xx@example.com/", | + | |
- | [Parameter(Mandatory)] | + | |
- | $path, | + | |
- | [Parameter(Mandatory)] | + | |
- | $text, | + | |
- | $wildcard = "*.*", | + | |
- | [Switch] | + | |
- | $pause = $False | + | |
- | ) | + | |
- | + | ||
- | function SearchDirectory ($session, $path, $wildcard, $text) | + | |
- | { | + | |
- | Write-Host ("Searching directory {0} ..." -f $path) | + | |
- | + | ||
- | $directoryInfo = $session.ListDirectory($path) | + | |
- | + | ||
- | foreach ($fileInfo in $directoryInfo.Files) | + | |
- | { | + | |
- | $filePath = ($path + "/" + $fileInfo.Name) | + | |
- | + | ||
- | if ($fileInfo.IsDirectory) | + | |
- | { | + | |
- | # Skip references to current and parent directories | + | |
- | if (($fileInfo.Name -ne ".") -and | + | |
- | ($fileInfo.Name -ne "..")) | + | |
- | { | + | |
- | # Recurse into subdirectory | + | |
- | SearchDirectory $session $filePath $wildcard $text | + | |
- | } | + | |
- | } | + | |
- | else | + | |
- | { | + | |
- | # Does file name match wildcard? | + | |
- | if ($fileInfo.Name -Like $wildcard) | + | |
- | { | + | |
- | # Action on match | + | |
- | + | ||
- | # Modify the code below if you want to do another task with | + | |
- | # matching files, instead of grepping their contents | + | |
- | + | ||
- | Write-Host ("File {0} matches mask, searching contents..." -f $filePath) | + | |
- | $tempPath = ($env:temp + "\" + $fileInfo.Name) | + | |
# Download file to temporary directory | # Download file to temporary directory | ||
- | $transferResult = $session.GetFiles($session.EscapeFileMask($filePath), $tempPath) | + | $filePath = [WinSCP.RemotePath]::EscapeFileMask($fileInfo.FullName) |
+ | $transferResult = $session.GetFiles($filePath, $tempPath) | ||
# Did the download succeeded? | # Did the download succeeded? | ||
if (!$transferResult.IsSuccess) | if (!$transferResult.IsSuccess) | ||
Line 194: | Line 103: | ||
foreach ($match in $matchInfo) | foreach ($match in $matchInfo) | ||
{ | { | ||
- | Write-Host ($filePath + ":" + $match.LineNumber + ":" + $match.Line) | + | Write-Host "$($fileInfo.FullName):$($match.LineNumber):$($match.Line)" |
} | } | ||
# Delete temporary local copy | # Delete temporary local copy | ||
Line 201: | Line 110: | ||
} | } | ||
} | } | ||
- | } | ||
- | } | ||
- | |||
- | try | ||
- | { | ||
- | # Load WinSCP .NET assembly | ||
- | Add-Type -Path "WinSCPnet.dll" | ||
- | |||
- | # Setup session options | ||
- | $sessionOptions = New-Object WinSCP.SessionOptions | ||
- | $sessionOptions.ParseUrl($sessionUrl) | ||
- | |||
- | $session = New-Object WinSCP.Session | ||
- | |||
- | try | ||
- | { | ||
- | # Connect | ||
- | $session.Open($sessionOptions) | ||
- | |||
- | # Start recursive search | ||
- | SearchDirectory $session $path $wildcard $text | ||
} | } | ||
finally | finally | ||
Line 228: | Line 116: | ||
$session.Dispose() | $session.Dispose() | ||
} | } | ||
- | · | + | |
$result = 0 | $result = 0 | ||
} | } | ||
- | catch [Exception] | + | catch |
{ | { | ||
- | Write-Host ("Error: {0}" -f $_.Exception.Message) | + | Write-Host "Error: $($_.Exception.Message)" |
$result = 1 | $result = 1 | ||
} | } | ||
Line 246: | Line 134: | ||
exit $result | exit $result | ||
</code> | </code> | ||
+ | |||
+ | ===== [[options]] Options ===== | ||
+ | |||
+ | &screenshotpict(extension_recursive_search_text) | ||
+ | |||
+ | In the //Text// box, specify the text to look for. The option is available when executing the extension only. | ||
+ | |||
+ | In the //File mask// box, specify a [[file_mask|file mask]] to select files. The option is available when executing the extension only. | ||
+ | |||
+ | 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. |