RFE: better INI inspectors / parsing with regular expressions as workaround

I’ve been dealing with several limitations in the “key of file” and “key of section of file” inspectors in parsing .INI and .INF files. The main issue being that it doesn’t seem possible to retrieve a list of Sections and list of Keys, without knowing the names of sections or keys beforehand.

This post contains some Regular Expressions I’m using to parse .INI files, rather then the built-in inspectors. These seem to work for me in my environment, but please give feedback if you know of any way to make them more reliable or efficient.

   Example File:

[SECTION1]
Signature="$Windows NT$"
Provider=%MANUFACTURER% 
ClassGUID={4D36E979-E325-11CE-BFC1-08002BE10318} 
Class=Printer 
CatalogFile=mydriver.cat
DriverVer=06/17/2014,61.170.01.18326
DriverIsolation=2

[MANUFACTURER]
%MANUFACTURER%=MYCOMPANY,NTAMD64
    
[MYCOMPANY.NTAMD64]
; Comment - Changing the order of Hardware Id entries to maintain uniformity in all PDL's
"Key 1" = "Value 1"
"Key 2" = "Value 2"
"Key 3" = "Value 3"
"Key 4" = "Value 4"
"Key 5" = "Value 5"

[SECTION4]
"Key 1" = "Value 1"
"Key 2" = "Value 2"

Operations:

// The "key of section of file" inspector is ok, if the names of the section and key and known
q: key "Class" of section "SECTION1" of file "C:\Temp\test.ini"
A: Printer
T: 0.324 ms

// Retrieve the section names if they are not known
q: substrings separated by "%0d%0a" of matches(regex("\[[^]]*\]")) of concatenation "%0d%0a" of lines whose (it as trimmed string does not start with ";") of file "C:\Temp\test.ini" as trimmed string
A: [SECTION1]
A: [MANUFACTURER]
A: [MYCOMPANY.NTAMD64]
A: [SECTION4]
T: 0.521 ms

// Key names within a known section, section name is case-insensitive "[MYCOMPANY.NTAMD64]"
q: preceding texts of firsts "=" of substrings separated by "%0d%0a" of matches(regex("\[[Mm][Yy][Cc][Oo][Mm][Pp][Aa][Nn][Yy][.][Nn][Tt][Aa][Mm][Dd]64\][^[]*")) of concatenation "%0d%0a" of lines whose (it as trimmed string does not start with ";") of file "C:\Temp\test.ini" as trimmed string
A: "Key 1"
A: "Key 2"
A: "Key 3"
A: "Key 4"
A: "Key 5"
T: 0.485 ms

// Values of all keys in the section
q: following texts of firsts "=" of substrings separated by "%0d%0a" of matches(regex("\[[Mm][Yy][Cc][Oo][Mm][Pp][Aa][Nn][Yy][.][Nn][Tt][Aa][Mm][Dd]64\][^[]*")) of concatenation "%0d%0a" of lines whose (it as trimmed string does not start with ";") of file "C:\Temp\test.ini" as trimmed string
A: "Value 1"
A: "Value 2"
A: "Value 3"
A: "Value 4"
A: "Value 5"
T: 0.455 ms

// Easier if the case of a section name can be assumed
q: following texts of firsts "=" of substrings separated by "%0d%0a" of matches(regex("\[SECTION4\][^[]*")) of concatenation "%0d%0a" of lines whose (it as trimmed string does not start with ";") of file "C:\Temp\test.ini" as trimmed string
A: Value 1
A: Value 2
T: 0.426 ms

// Full content of a section with a known name
q: substrings  separated by "%0d%0a" whose (it as trimmed string != "" and it contains "=") of matches(regex("\[[Mm][Yy][Cc][Oo][Mm][Pp][Aa][Nn][Yy][.][Nn][Tt][Aa][Mm][Dd]64\][^[]*")) of concatenation "%0d%0a" of lines whose (it as trimmed string does not start with ";") of file "C:\Temp\test.ini" as trimmed string
A: "Key 1" = "Value 1"
A: "Key 2" = "Value 2"
A: "Key 3" = "Value 3"
A: "Key 4" = "Value 4"
A: "Key 5" = "Value 5"
T: 2.218 ms

Notes -
Concatenate the lines of a file with the %0d%0a (CR/LF) characters to maintain a “natural” escape formatting, and so we can read multiple lines in a single regex.
The “%0d%0a” string is unlikely to exist in the file, but if it does, BigFix escapes it as %250d%250a anyway.
Exclude lines whose first character is “;” - these are Comments in .ini format
After matching a regex, split the results using the %0d%0a characters that we originally used to join the lines
After splitting a result, use the “trimmed string” cast to remove any preceding/following whitespace

1 Like

I agree, I have come up against those same limitations of the INI inspectors, and it is very annoying. There definitely should be an RFE for better/improved INI inspectors.

I am doing some INI inspection here:

http://bigfix.me/analysis/details/2994748

Sections of files:

(it as string as trimmed string) whose(it starts with "[" AND it ends with "]") of lines of files

I don’t like to use regex if I don’t need to. I think it is less human readable.


You should use case insensitive regex instead of regex in most of the above cases.


All keys of all sections: ( I realize this isn’t what you are looking for, but useful in some cases )

unique values of (it as trimmed string) of preceding texts of firsts "=" of lines whose (it as trimmed string does not start with ";" AND it contains "=") of files "C:\Temp\test.ini"

(James is too quick…but I was also frying an onion during this reply.)

I was going to also suggest the use of “case insensitive regex” to simplify the regex.

substrings  separated by "%0d%0a" whose (it as trimmed string != "" and it contains "=") of matches(case insensitive regex("\[MyCOMPANY.NTAMD64\][^[]*")) of concatenation "%0d%0a" of lines whose (it as trimmed string does not start with ";") of file "C:\Temp\test.ini"

If you wanted to get the contents of the known named section without using a regex, then this should work:

substrings separated by "|" of preceding text of first "|[" of following text of first "[MYCOMPANY.NTAMD64]|" of (it & "|[") of concatenation "|" of lines whose (it as trimmed string does not start with ";" and it does not start with " ") of file "C:\Temp\test.ini"
1 Like

Thanks to both of you, I had completely overlooked case insensitive regex (as I built most of the regex strings in another tool to begin with).

1 Like

So I’ve been updating the code in this area on multiple platforms for other reasons but what are all these INI’s you’re looking at? The INI’s supposed to have gone the way of the do-do but I take it everyone is finding reasons to use them.

1 Like

The INI format is used by various programs and many areas in windows, but especially drivers.

All of the following are in INI format: (windows only)

files whose(name of it as lowercase ends with ".inf") of folders of folders "DriverStore\FileRepository" of system folders

I actually prefer INI files for human readable / editable config files. The clientsettings.cfg file is similar but without a section heading. I have never tried using the existing inspectors to read key/values that are not in a section, does that work?

Also, this should work, but doesn’t:

sections whose(name of it as lowercase = "whatever") of files

Yes you can read key=value out of a sectionless file.

And yes the whose won’t work as there are no iterators at the moment, meaning nothing that you can go sections of <file>

1 Like

Exactly, the case I am working through right now is parsing .inf files for printer installations, determining which printer model name is supplied by each .inf, and using Microsoft’s print driver .vbs script to install them.

Iterators for the key and section inspectors would be very helpful.

1 Like

I have done some work around drivers INF files in general, and specifically for printers & scanners. Most of it should be on BigFix.me somewhere.

Have you tried using variables of file inspector?

// the variables of file inspector parses ini files, and produces strings in the form of [sectionname].name=value
// for your examples, it produces
q: variables of file "C:\Temp\test.ini"
A: [SECTION1].Signature="$Windows NT$"
A: [SECTION1].Provider=%25MANUFACTURER%25
A: [SECTION1].ClassGUID={4D36E979-E325-11CE-BFC1-08002BE10318}
A: [SECTION1].Class=Printer
A: [SECTION1].CatalogFile=mydriver.cat
A: [SECTION1].DriverVer=06/17/2014,61.170.01.18326
A: [SECTION1].DriverIsolation=2
A: [MANUFACTURER].%25MANUFACTURER%25=MYCOMPANY,NTAMD64
A: [MYCOMPANY.NTAMD64]."Key 1"="Value 1"
A: [MYCOMPANY.NTAMD64]."Key 2"="Value 2"
A: [MYCOMPANY.NTAMD64]."Key 3"="Value 3"
A: [MYCOMPANY.NTAMD64]."Key 4"="Value 4"
A: [MYCOMPANY.NTAMD64]."Key 5"="Value 5"
A: [SECTION4]."Key 1"="Value 1"
A: [SECTION4]."Key 2"="Value 2"
T: 0.435 ms

// here are your relevances, followed by how you would get the same thing using 'variables of <file>'
// The "key of section of file" inspector is ok, if the names of the section and key and known
q: key "Class" of section "SECTION1" of file "C:\Temp\test.ini"
A: Printer
T: 0.560 ms

// using  'variables'
q: following text of first "=" of variables whose ( it starts with "[SECTION1].Class=") of file "C:\Temp\test.ini"
A: Printer
T: 0.456 ms

// Retrieve the section names if they are not known
q: substrings separated by "%0d%0a" of matches(regex("\[[^]]*\]")) of concatenation "%0d%0a" of lines whose (it as trimmed string does not start with ";") of file "C:\Temp\test.ini" as trimmed string
A: [SECTION1]
A: [MANUFACTURER]
A: [MYCOMPANY.NTAMD64]
A: [SECTION4]
T: 0.677 ms

// using 'variables'
q: (it & "]") of unique values of (preceding text of first "]." of it ) of variables of file "C:\Temp\test.ini"
A: [MANUFACTURER]
A: [MYCOMPANY.NTAMD64]
A: [SECTION1]
A: [SECTION4]
T: 0.438 ms

// Key names within a known section, section name is case-insensitive "[MYCOMPANY.NTAMD64]"
q: preceding texts of firsts "=" of substrings separated by "%0d%0a" of matches(regex("\[[Mm][Yy][Cc][Oo][Mm][Pp][Aa][Nn][Yy][.][Nn][Tt][Aa][Mm][Dd]64\][^[]*")) of concatenation "%0d%0a" of lines whose (it as trimmed string does not start with ";") of file "C:\Temp\test.ini" as trimmed string
A: "Key 1"
A: "Key 2"
A: "Key 3"
A: "Key 4"
A: "Key 5"
T: 0.924 ms

// using variables
q: (following text of first "]." of preceding texts of first "=" of it) of variables whose ( it as uppercase starts with "[MYCOMPANY.NTAMD64]") of file "C:\Temp\test.ini"
A: "Key 1"
A: "Key 2"
A: "Key 3"
A: "Key 4"
A: "Key 5"
T: 0.476 ms

// Values of all keys in the section
q: following texts of firsts "=" of substrings separated by "%0d%0a" of matches(regex("\[[Mm][Yy][Cc][Oo][Mm][Pp][Aa][Nn][Yy][.][Nn][Tt][Aa][Mm][Dd]64\][^[]*")) of concatenation "%0d%0a" of lines whose (it as trimmed string does not start with ";") of file "C:\Temp\test.ini" as trimmed string
A: "Value 1"
A: "Value 2"
A: "Value 3"
A: "Value 4"
A: "Value 5"
T: 0.680 ms

// using variables
q: (following text of first "=" of it) of variables whose ( it as uppercase starts with "[MYCOMPANY.NTAMD64]") of file "C:\Temp\test.ini"
A: "Value 1"
A: "Value 2"
A: "Value 3"
A: "Value 4"
A: "Value 5"
T: 0.423 ms

// Easier if the case of a section name can be assumed
q: following texts of firsts "=" of substrings separated by "%0d%0a" of matches(regex("\[SECTION4\][^[]*")) of concatenation "%0d%0a" of lines whose (it as trimmed string does not start with ";") of file "C:\Temp\test.ini" as trimmed string
A: "Value 1"
A: "Value 2"
T: 0.643 ms

// using variables
q: (following text of first "=" of it) of variables whose ( it starts with "[SECTION4]") of file "C:\Temp\test.ini"
A: "Value 1"
A: "Value 2"
T: 0.452 ms

// Full content of a section with a known name
q: substrings  separated by "%0d%0a" whose (it as trimmed string != "" and it contains "=") of matches(regex("\[[Mm][Yy][Cc][Oo][Mm][Pp][Aa][Nn][Yy][.][Nn][Tt][Aa][Mm][Dd]64\][^[]*")) of concatenation "%0d%0a" of lines whose (it as trimmed string does not start with ";") of file "C:\Temp\test.ini" as trimmed string
A: "Key 1" = "Value 1"
A: "Key 2" = "Value 2"
A: "Key 3" = "Value 3"
A: "Key 4" = "Value 4"
A: "Key 5" = "Value 5"
T: 0.777 ms

// using  'variables'
q: following texts of firsts "]." of variables whose ( it as uppercase starts with "[MYCOMPANY.NTAMD64]") of file "C:\Temp\test.ini"
A: "Key 1"="Value 1"
A: "Key 2"="Value 2"
A: "Key 3"="Value 3"
A: "Key 4"="Value 4"
A: "Key 5"="Value 5"
T: 0.542 ms
2 Likes

Um…well…uh. That’s awesome! Never noticed that we could use “variables of file”, I’ve only looked at “variables of environment” before.

I think that will make some things considerably easier for me, thanks much!

1 Like

Wow, I never knew about that. I think that is exactly what @JasonWalker and I were looking for… though being able to iterate over the sections would still be useful, this will help a lot.