Home > Enterprise >  Powershell regex gis flags support
Powershell regex gis flags support

Time:05-23

I want to use this regex in Powershell which uses gis flags,

https://regex101.com/r/yoM4cV/1

/(?<=#start)(. )(?=#end)/gis

to match sentence between #start and #end

  #start
  hello powershell regex
  what is the equivalent of flags gis
  #end

I cannot find any info about gis flags support for Powershell. Is this missing in Powershell ? If yes, what's the alternative ?

update : I ask because this returns false

  $test=@'
  #start
  hello powershell regex
  what is the equivalent of flags gis
  #end
  '@

  $test -match (?<=#start)(. )(?=#end)

CodePudding user response:

Regexes in PowerShell are defined with string literals, not regex literals (structures like action/pattern/flags).

The g flag is not implemented as a regex option in PowerShell, you need to use mutliple matching. If you use Select-String, you will need -AllMatches option to match all occurrences. If you use the .NET regex API, ypu can use the Regex.Matches method that finds all occurrences.

The i and s flag usage is simpler, you may just use them as inline modifier, (?s), (?i), or joined as (?si) that you can place at the start of the pattern so that they could affect the whole pattern (or place them anywhere in the pattern so that to affect all the pattern to the right of them (and you can disable them with (?-s)/(?-i)/(?-si))).

If you define a regex object, you can use [Text.RegularExpressions.RegexOptions]'Singleline', [Text.RegularExpressions.RegexOptions]'IgnoreCase', or [Text.RegularExpressions.RegexOptions]'IgnoreCase, Singleline'.

When you need to use s flag, you must make sure your input is treated as a single multiline string. If you read files in, you need to make sure you use -Raw option with Get-Content.

Here is an example of the gis flag "conversion" in PowerShell:

$rx = [System.Text.RegularExpressions.Regex]::new('(?<=#start)(.*?)(?=#end)', [Text.RegularExpressions.RegexOptions]'IgnoreCase, Singleline')
$test=@'
   #start
   hello powershell regex
   what is the equivalent of flags gis
   #end
'@
$rx.Matches($test).value

Output:

  hello powershell regex
  what is the equivalent of flags gis

The same result can be obtained with

($test | Select-String -Pattern '(?si)(?<=#start)(.*?)(?=#end)' -AllMatches).Matches.Value

CodePudding user response:

To fix your example, you need to write it like this:

$test -match '(?s)(?<=#start)(. )(?=#end)'
  • Enable single-line mode (?s) to allow . to match over multiple lines.
  • Quote the regular expression because () is the PowerShell grouping operator which in your example makes PowerShell try to interpret the regular expression pattern as PowerShell expressions, which does not work. Instead, pass it as literal string to -match.

Detailed explanation:

From gis, only i (case-insensitive) and s (single-line) are available as flags in PowerShell (or more generally in .NET, because PowerShell uses .NET RegEx support under the hood).

You can specify these either as inline flags at the beginning of the pattern ...

(?is)(?<=#start)(. )(?=#end)

... or by using the options parameter of the Regex class, which accepts a RegExOptions argument:

$options = [System.Text.RegularExpressions.RegexOptions] 'IgnoreCase, Singleline'
[Regex]::Match('text', 'pattern', $options)

The g (global) flag is not directly available. You can use it effectively by calling the [Regex]::Matches() method, which finds all matches (contrary to [Regex]::Match() and also the -match operator which find only the first match). Its output is a collection of all matches that have been found.

Alternatively, you can use Select-String with parameter -AllMatches, as Wiktor Stribiżew's helpful answer demonstrates.

For demonstration purposes I've slightly modified your example so that it actually finds multiple matches (your original RegEx matches everything between first #start to last #end, so it only ever finds one match).

$text = @'
#start
hello powershell regex
what is the equivalent of flags gis
#end
#start
foobar
#end
'@

$pattern = '(?is)(?<=#start)(. ?)(?=#end)'

foreach( $match in [Regex]::Matches($text, $pattern) ) {
    '----- Match -----'
    $match.Groups[1].Value   # Output the captured value of the 1st group
}

Output:

----- Match -----

hello powershell regex
what is the equivalent of flags gis

----- Match -----

foobar
  • Related