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()
(Get-Content -ReadCount 0
is in essence the PowerShell equivalent of [System.IO.File]::ReadAllLines()
):
[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.
Similarly, if you expect the last matching line to be close(r) to the end of the large file, you'll need to lazily read the file backwards for best performance, for which there is no standard .NET API. Doing so in a way that handles variable-width character encodings such as UTF-8 is nontrivial, however - see this answer.