Public/Select-Copy.ps1
|
function Select-Copy { [CmdletBinding(DefaultParameterSetName='Slice')] [Alias('scopy')] param( [Parameter(ParameterSetName='Slice')] [int]$First, [Parameter(ParameterSetName='Slice')] [int]$Last, [Parameter(ParameterSetName='Slice')] [int[]]$Range, [Parameter(ParameterSetName='Search', Mandatory=$true)] [string]$Pattern, [Parameter(ParameterSetName='Search')] [int]$Context = 0, [Parameter(ParameterSetName='Search')] [switch]$Merge = $true, [Parameter(ValueFromPipeline=$true)] [psobject]$InputObject, [switch]$PassThru ) Begin { $lines = [System.Collections.Generic.List[string]]::new() } Process { if ($null -ne $InputObject) { # Normalize input: handle strings, arrays, and split by newlines # If InputObject is not a string, try to convert it, but usually it's string from logs $content = if ($InputObject -is [string]) { $InputObject } else { $InputObject.ToString() } # Split by regex to handle mixed line endings $split = $content -split '\r?\n' foreach ($line in $split) { $lines.Add($line) } } } End { $allLines = $lines.ToArray() $count = $allLines.Count $selectedIndices = [System.Collections.Generic.HashSet[int]]::new() if ($PSCmdlet.ParameterSetName -eq 'Slice') { $hasSliceParam = $PSBoundParameters.ContainsKey('First') -or $PSBoundParameters.ContainsKey('Last') -or $PSBoundParameters.ContainsKey('Range') if (-not $hasSliceParam) { # If no slice parameters, select all for ($i = 0; $i -lt $count; $i++) { [void]$selectedIndices.Add($i) } } else { # First N if ($PSBoundParameters.ContainsKey('First')) { $limit = [Math]::Min($First, $count) for ($i = 0; $i -lt $limit; $i++) { [void]$selectedIndices.Add($i) } } # Last N if ($PSBoundParameters.ContainsKey('Last')) { $start = [Math]::Max(0, $count - $Last) for ($i = $start; $i -lt $count; $i++) { [void]$selectedIndices.Add($i) } } # Range (Indices) if ($PSBoundParameters.ContainsKey('Range')) { foreach ($r in $Range) { if ($r -ge 0 -and $r -lt $count) { [void]$selectedIndices.Add($r) } } } } } elseif ($PSCmdlet.ParameterSetName -eq 'Search') { # Find matches $matchIndices = [System.Collections.Generic.List[int]]::new() for ($i = 0; $i -lt $count; $i++) { if ($allLines[$i] -match $Pattern) { $matchIndices.Add($i) } } # Apply Context foreach ($idx in $matchIndices) { $start = [Math]::Max(0, $idx - $Context) $end = [Math]::Min($count - 1, $idx + $Context) for ($k = $start; $k -le $end; $k++) { [void]$selectedIndices.Add($k) } } } # Sort indices to reconstruct order $sortedIndices = $selectedIndices | Sort-Object # Build Output $outputLines = [System.Collections.Generic.List[string]]::new() if ($PSCmdlet.ParameterSetName -eq 'Search' -and -not $Merge) { # Insert separators between non-contiguous blocks for ($i = 0; $i -lt $sortedIndices.Count; $i++) { $currentIndex = $sortedIndices[$i] if ($i -gt 0) { $prevIndex = $sortedIndices[$i-1] # If there is a gap > 1, insert separator # Gap of 1 means they are adjacent (e.g. 10, 11), so no separator. if ($currentIndex -gt $prevIndex + 1) { $outputLines.Add("---") } } $outputLines.Add($allLines[$currentIndex]) } } else { # Just add lines foreach ($idx in $sortedIndices) { $outputLines.Add($allLines[$idx]) } } $resultText = $outputLines -join [Environment]::NewLine # Send to Clipboard if ($resultText) { Set-Clipboard -Value $resultText } # PassThru if ($PassThru) { Write-Output $resultText } } } |