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 ifstuff
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 thatstuff
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