Select-String: match a string only if it isn't preceded by a specific character Select-String: match a string only if it isn't preceded by a specific character powershell powershell

Select-String: match a string only if it isn't preceded by a specific character


You cannot perform the desired matching with literal substring searches (-SimpleMatch).

Instead, use a regex with a negative look-behind assertion ((?<!..)) to rule out stuff substrings preceded by a ; char.: (?<!;)stuff

Applied to your command:

Get-Content "C:\temp\list\list.txt" |   Where-Object { Select-String -Quiet -Pattern '(?<!;)stuff' -LiteralPath $_ }

Regex pitfalls:

  • It is tempting to use [^;]stuff instead, using a negated (^) character set ([...]) (see this answer); however, this will not work as expected if stuff appears at the very start of a line, because a character set - whether negated or not - only matches an actual character, not the start-of-the-line position.

  • It is then tempting to apply ? to the negated character set (for an optional match - 0 or 1 occurrence): [^;]?stuff. However, that would match a string containing ;stuff again, given that stuff is technically preceded by a "0-repeat occurrence" of the negated character set; thus, ';stuff' -match '[^;]?stuff' yields $true.

Only a look-behind assertion works properly in this case - see regular-expressions.info.


To complement @mklement0's answer, I suggest an alternative approach to make your code easier to read and understand:

#requires -Version 4@(Get-Content -Path 'C:\Temp\list\list.txt').    ForEach([IO.FileInfo]).    Where({ $PSItem | Select-String -Pattern '(?<!;)stuff' -Quiet })

This will turn your strings into objects (System.IO.FilePath) and utilizes the array functions ForEach and Where for brevity/conciseness. Further, this allows you to pipe the paths as objects which will be accepted by the -Path parameter into Select-String to make it more understandable (I find long lists of parameter sets difficult to read).


The example code posted won't actually run, as it will look at each line as the -Path value.

What you need is to get the content, select the string you're after, then filter the results with Where-Object

Get-Content "C:\temp\list\list.txt" | Select-String -Pattern "stuff" | Where-Object {$_ -notmatch ";stuff"}

You could create a more complex regex if needed, but depends on what your result data from your files looks like