2

This regex: (?is)[\s\S]*?\[General\][\s\S]*?SystemMustBeRebooted=(\d)[\s\S]*?\[Install Execution\][\s\S]*?SilentInstall=""(.*?)"".* doesn't match the below cut down part of the file in Powershell. I need to extract:

  1. SystemMustBeRebooted value
  2. The SilentInstall value

It DOES work however, on regex101.com (with removed (?is)), using .NET regex, AND regexr.com set to Powershell regex none the less!

Here is the result when relevant code lines are run in Powershell:

$CVAFileContents = get-content $($CVAFile).fullname -raw

$RebootNeededandSilentInstall = $CVAFileContents | select string -pattern '(?s)[\s\S]*?\[General\][\s\S]*?SystemMustBeRebooted=(\d)[\s\S]*?\[Install Execution\][\s\S]*?SilentInstall="(.*?)".*' -allmatches

$RebootNeededandSilentInstall

<back to PS prompt>

If I cut the regex back to [General], it matches the below. Anything more added, no results though.

[General]
PN=P01759-B2M
Version=24.9764.1433.30
Revision=Q
Pass=5
Type=Driver
Category=Driver-Audio
SystemMustBeRebooted=0

....

[Install Execution]
Install="HPUP.exe"
SilentInstall="HPUP.exe"

What is wrong with my regex??

EDIT: Of course it works when using -match (without the thought-to-be-needed double escaping of "):

$CVAFileContents -match '(?s)[\s\S]*?\[General\][\s\S]*?SystemMustBeRebooted=(\d)[\s\S]*?\[Install Execution\][\s\S]*?SilentInstall="(.*?)".*' | out-null

Produces:

$Matches

Name                           Value                                                                                                                                     
----                           -----                                                                                                                                     
2                              HPUP.exe                                                                                                                                  
1                              0                                                                                                                                         
0                              [CVA File Information]...       

Just for curiousity, why does the same expression work with -match and not select-string??

12
  • 2
    You have an .INI file, so better yreat it as one. There are several good modules for that around, like this one
    – Theo
    Commented Jan 24 at 19:26
  • @Theo That isn't an ini file. I realise it looks like one. It needs to be parsed by Powershell, and not used by Windows.
    – user66001
    Commented Jan 24 at 19:38
  • @sln Accepts it if I reduce it down to what comes before and including [General]. Not if I add anything after. I would really like to know why it works fine on two regex checker sites, set to .NET or Powershell explicitly, and using -match, but not in full form using select string...
    – user66001
    Commented Jan 24 at 19:46
  • "Parsed into my intended regex"? Yeah, probably could get rid of the ending .*, but with and without doesn't make a difference, in any place the regex works/doesn't.
    – user66001
    Commented Jan 24 at 19:51
  • 1
    So why is this not an ini file? Did you leave out parts that can be relevant?
    – Theo
    Commented Jan 24 at 19:52

2 Answers 2

4

What is wrong with my regex??

Nothing, though it can be simplified (see below).

why does the same expression work with -match and not select-string??

It does work with Select-String too, you just need to extract the results differently:

$rebootNeeded, $silentInstallExe =
  $CVAFileContents | 
    Select-String '(?s)\[General\].*?SystemMustBeRebooted=(\d).*?\[Install Execution\].*?SilentInstall="(.*?)"' |
    ForEach-Object { ($_.Matches[0].Groups | Select-Object -Skip 1).Value }
  • That is, Select-String emits a Microsoft.PowerShell.Commands.MatchInfo instance for each matching input string, from which you can extract the capture-group matches.

  • In your case, there is only one input string, namely the entire content of your input file (due to having read the file with Get-Content's -Raw switch).

  • Note that the -AllMatches switch has been omitted above, because you only need it to find multiple matches of your pattern in each input string, which doesn't apply here.

  • The absence of -AllMatches implies that the $_.Matches collection contains only one System.Text.RegularExpressions.Match instance describing the match.

  • The .Groups collection's first entry ([0]) is always the overall match, whereas the subsequent entries represent the capture-group matches; Select-Object -Skip 1 skips the overall match, and (...).Value uses member-access enumeration to extract the captured text from the groups.

  • Finally, the two pieces of text captured by the capture groups are assigned to two separate output variables, $rebootNeeded and $silentInstallExe, using a multi-assignment.


That said, if you only need to look for one match and if the only information you need is what text the capture groups captured, using -match, the regular-expression matching operator, which reflects the details of the match in the automatic $Matches variable, is the simpler and more efficient choice.

2
  • 2
    Even though this is likely to be deleted. THANKS!
    – user66001
    Commented Jan 24 at 23:13
  • Glad to hear it helped, @user66001
    – mklement0
    Commented Jan 25 at 1:53
-1

Just to let you know the .CVA files are in fact text files in the .INI format (albeit with quirks in the sense that comments are not preceeded by a ';' semicolon or a '#' hash sign), you can simply read the values you need using an INI reader like the one below I've written some time ago:

function Import-Ini {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [Alias('FullName', 'FilePath')]
        [string]$Path
    )

    # PowerShell < 3.0
    # $ini = New-Object System.Collections.Specialized.OrderedDictionary([System.StringComparer]::OrdinalIgnoreCase)
    $ini     = [ordered]@{}
    $section = $null

    # regex to remove trailing inline comments unless quoted
    $trailingComment = '[;#](?=([^"]*"[^"]*")*[^"]*$).*$'

    switch -Regex -File $Path {
        # skip comment and empty or whitespace only lines
        '^\s*$'       {}
        '^\s*[;#].*$' {}

        # a section
        '^\s*\[(.+)\]\s*$' {
            $section = $matches[1].Trim()
            if (!($ini[$section])) { $ini[$section] = [ordered]@{} }
        }

        # a property: Key = Value
        '^\s*([^;#]+?)\s*=\s*(.*)' {
            if (($section) -and !($ini[$section])) { $ini[$section] = [ordered]@{} }
            # remove inline trailing comment, trim and unquote (by both single or double quote characters)
            $name, $value = ($matches[1..2] -replace $trailingComment).Trim() -replace '^(?:"(.*)"|''(.*)'')$', '$1$2'
            if ($name.EndsWith('[]')) {
                # it's an array
                $name = $name.TrimEnd('[]').Trim()
                if (!$section) {
                    # no section? just add to the ini hash as array
                    if (!$ini[$name]) { $ini[$name] = @() }
                    $ini[$name] += $value
                }
                else {
                    if (!$ini[$section][$name]) { $ini[$section][$name] = @() }
                    $ini[$section][$name] += $value
                }
            }
            else {
                if (!$section) { $ini[$name] = $value }
                else { $ini[$section][$name] = $value }
            }
        }

        # bad entry
        default {
            # comment out the next line if you don't want to see the warnings
            # or append  -WarningAction SilentlyContinue to the Import-Ini command
            Write-Warning "Bad entry detected: '$_'"
        }
    }

    return $ini
}

Using a .CVA test file I have downloaded from here

you can simply do:

$ini = 'D:\Test\sp110416.cva' | Import-Ini -WarningAction SilentlyContinue
# get the values
$ini.General.SystemMustBeRebooted       # --> 0
$ini.'Install Execution'.SilentInstall  # --> "HPImageAssistant.exe" /opendir /launch
3
  • That is a LOT of code to replace a regex with. Hopefully it helps someone else. As an aside, I am not sure that not confirming to INI format with comments makes a file that is otherwise confirming an INI file. Just like if I use parts of a XML format, doesn't make it XML.
    – user66001
    Commented Jan 26 at 4:39
  • @user66001 You can give a file any extension you want, but the content or format does not change by doing so. CVA is just INI. Btw why downvote the answer when it shows how reliably you can get the individual variables in the file?
    – Theo
    Commented Jan 26 at 14:45
  • I have tried to load up this "INI" file through Windows, and it doesn't do anything. It may have the same format as an INI file, but I trust renamed CVA because they didn't want it run as an "INI" file, because of my result. I downvoted because I asked what was wrong with my regex, not what other longer solution can be provided. If you choose to keep it up, hopefully it will help another not wanting to use regex, and only regex, in their need.
    – user66001
    Commented Jan 27 at 14:07

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.