Is there a way to get the line index of the last occurrence in file, not the line itself?
$delegate = [Func[string,bool]] {$args[0] -match $myString}
$lastCheckIn = [Linq.Enumerable]::Last([System.IO.File]::ReadLines($myFile), $delegate)
I thought of using [Linq.Enumerable]::Count but couldn't find a method to return a sequence from the start to $lastCheckIn.
CodePudding user response:
Limiting the approach to Linq, you can try the following.
$lines = 'aaa','bbb','ccc','ddd','aaa','bbb','ccc','ddd'
$searchPattern = 'c '
$selectDelegate = [Func[object, int32, object]] { @{line=$args[0]; index=$args[1] } }
$whereDelegate = [Func[object,bool]] { $args[0].line -match $searchPattern }
$objects = [Linq.Enumerable]::Select($lines, $selectDelegate)
$lastObject = [Linq.Enumerable]::Last($objects, $whereDelegate)
$lastObject.index
# result = 6 (zero-based index)
# Without Linq
$lines | Select-String $searchPattern | Select-Object -Last 1 -ExpandProperty LineNumber
# result = 7 (one-based index)
This first builds up a collection of line/index pairs, filters for the last match, and then extract the index. I've also included a non-linq powershell equivalent.
CodePudding user response:
I believe this should be faster and easier to implement, maintain and understand:
$pattern = 'yourpatternhere'
$content = [System.IO.File]::ReadAllLines('path/to/file.ext')
$tail = $content.Count
while($tail--)
{
if($content[$tail] -match $pattern)
{
"$pattern was found on line: $tail"
break
}
}
CodePudding user response:
T N's helpful answer shows an effective LINQ solution as well as a PowerShell-idiomatic alternative.
There are two alternatives for improved performance:
- If it is acceptable to load the entire file into memory first, use
[Array]::FindLastIndex():
[Array]::FindLastIndex(
(Get-Content -ReadCount 0 $myFile), # -ReadCount 0 returns all lines as single array
[Predicate[string]] { $args[0] -match $myString }
)
- An optimized LINQ solution, with lazy enumeration:
$i = $index = -1
$null = [Linq.Enumerable]::LastOrDefault(
[IO.File]::ReadLines($myFile),
[Func[string, bool]] {
$script:i;
if ($args[0] -match $myString) { $script:index = $script:i; return $true }
}
)
$index # output the index of the last match, if not found, -1
Caveat: This approach to finding the index only works as intended with lazy enumerables as input, as only they necessitate forward enumeration until the very last element.
By contrast, list-like enumerables (those that implement the System.Collections.IList interface or its generic counterpart), are enumerated backwards, from the end of the list, for optimized performance.
