powershellselect-stringexact-match

How to make PowerShell `Select-String` aka `sls` output exact match(es) only?


In a PowerShell script, I would like to check if a branch exists locally already, which I'm currently doing as $existsInLocal = (git branch | sls $branch) -ne $null, but that's incorrect because sls $branch matches line(s) containing $branch as a substring, but what I need is a version of sls $branch that outputs line(s) matching $branch exactly.

Is there a way to use ^ and $ to indicate start and end of line, respectively? My attempts at doing so didn't succeed.

I have looked at the Select-String documentation and didn't find a solution.

There's also an almost solution here that makes use of \b which matches a word boundary, but it doesn't work for my case because branch names can have \W chars (e.g. / or -). For example, \bjose/feature\b would match dev/jose/feature (the first \b matches the transition from / to j) or jose/feature/v2 or dev/jose/feature/v2.


I know there's another way to check for existence of local branch, but I really am mostly interested in sls exact matches in general, and the "determine whether local branch exists" use case is just one of many.


Solution

  • To address your general question:

    I really am mostly interested if sls (Select-String) finds exact matches in general.


    Applied to your case:

    $existsInLocal = git branch | sls -Quiet ('^\s*{0}$' -f [regex]::Escape($branch))
    

    Taking a step back:

    As mclayton notes, a simpler solution for literal whole-line matching is possible, using the -contains operator:

    $existsInLocal = (git branch) -contains $branch
    

    The only potential down-side of this approach is that it requires collecting all stdout lines in memory, up front before matching is performed, whereas the streaming nature of a Select-String-based pipeline solution allows stopping as soon as a match is found.
    With only a limited amount of stdout output, such as in the case at hand, this won't be a problem, however.


    [1] The typical use case is that Select-String receives text line by line, such as by making it search a file using -Path / -LiteralPath or when piping the output of an external program to it.
    However, when you pass a multiline string as a single object to it (e.g. by piping Get-Content -Raw to it), that string is searched through as a whole, enabling cross-line matching.