Here my solution to parse INI file with Powershell :
config.ini:
[readme]
; This is a sample configuration file
; Comments start with ';', as in php.ini
[asset]
; Allows you to select assets based on custom fields
environnement_field = "Environnement"
environnement_values[] = "production"
decommissioned_field = "Deco"
decommissioned_values[] = "oui"
decommissioned_values[] = "yes"
decommissioned_values[] = "vrai"
decommissioned_values[] = "true"
[form]
second_section[one] = "1 associated"
second_section[two] = "2 associated"
second_section[] = "1 unassociated"
second_section[] = "2 unassociated"
second_section[] = 1
second_section[] = 1.3
second_section[] = 2.2
[second_section]
path = "/usr/local/bin"
URL = "http://www.example.com/~username"
second_section[one] = "1 associated"
second_section[two] = "2 associated"
second_section[three] = 3
second_section[four] = 4.4
second_section[] = "1 unassociated"
second_section[] = "2 unassociated"
second_section[] = 1
second_section[] = 2.2
[third_section]
phpversion[] = "5.0"
phpversion[] = "5.1"
phpversion[] = "5.2"
phpversion[] = "5.3"
my_script.ps1 :
$PSScriptRoot
function Parse-IniFile ($filePath)
{
$ini = [ordered]@{}
$count = @{}
switch -regex -file $filePath
{
#Section.
"^\[(.+)\]$"
{
$section = $matches[1].Trim()
$ini[$section] = [ordered]@{}
$count[$section] = @{}
$CommentCount = 0
continue
}
# Comment
"^(;.*)$"
{
$value = $matches[1]
$CommentCount = $CommentCount + 1
$name = "Comment" + $CommentCount
if ($section -eq $null) {
$section = "header"
$ini[$section] = [ordered]@{}
}
$ini[$section][$name] = $value
continue
}
#Array Int.
"^\s*([^#][\w\d_-]+?)\[]\s*=\s*(\d+)\s*$"
{
$name,$value = $matches[1..2]
if (!$ini[$section][$name]) {
$ini[$section][$name] = [ordered]@{}
}
if (!$count[$section][$name]) {
$count[$section][$name] = 0
}
$ini[$section][$name].Add($count[$section][$name], [int]$value)
$count[$section][$name] += 1
continue
}
#Array Decimal
"^\s*([^#][\w\d_-]+?)\[]\s*=\s*(\d+\.\d+)\s*$"
{
$name,$value = $matches[1..2]
if (!$ini[$section][$name]) {
$ini[$section][$name] = [ordered]@{}
}
if (!$count[$section][$name]) {
$count[$section][$name] = 0
}
$ini[$section][$name].Add($count[$section][$name], [decimal]$value)
$count[$section][$name] += 1
continue
}
#Array Everything else
"^\s*([^#][\w\d_-]+?)\[]\s*=\s*(.*)"
{
$name,$value = $matches[1..2]
if (!$ini[$section][$name]) {
$ini[$section][$name] = [ordered]@{}
}
if (!$count[$section][$name]) {
$count[$section][$name] = 0
}
$ini[$section][$name].Add($count[$section][$name], $value.Trim())
$count[$section][$name] += 1
continue
}
#Array associated Int.
"^\s*([^#][\w\d_-]+?)\[([\w\d_-]+?)]\s*=\s*(\d+)\s*$"
{
$name, $association, $value = $matches[1..3]
if (!$ini[$section][$name]) {
$ini[$section][$name] = [ordered]@{}
}
$ini[$section][$name].Add($association, [int]$value)
continue
}
#Array associated Decimal
"^\s*([^#][\w\d_-]+?)\[([\w\d_-]+?)]\s*=\s*(\d+\.\d+)\s*$"
{
$name, $association, $value = $matches[1..3]
if (!$ini[$section][$name]) {
$ini[$section][$name] = [ordered]@{}
}
$ini[$section][$name].Add($association, [decimal]$value)
continue
}
#Array associated Everything else
"^\s*([^#][\w\d_-]+?)\[([\w\d_-]+?)]\s*=\s*(.*)"
{
$name, $association, $value = $matches[1..3]
if (!$ini[$section][$name]) {
$ini[$section][$name] = [ordered]@{}
}
$ini[$section][$name].Add($association, $value.Trim())
continue
}
#Int.
"^\s*([^#][\w\d_-]+?)\s*=\s*(\d+)\s*$"
{
$name,$value = $matches[1..2]
$ini[$section][$name] = [int]$value
continue
}
#Decimal.
"^\s*([^#][\w\d_-]+?)\s*=\s*(\d+\.\d+)\s*$"
{
$name,$value = $matches[1..2]
$ini[$section][$name] = [decimal]$value
continue
}
#Everything else.
"^\s*([^#][\w\d_-]+?)\s*=\s*(.*)"
{
$name,$value = $matches[1..2]
$ini[$section][$name] = $value.Trim()
continue
}
}
return $ini
}
function Set-IniFile ($ini, $filePath)
{
$output = @()
foreach($section in $ini.Keys)
{
# Put a newline before category as seperator, only if there is null
$seperator = if ($output[$output.Count - 1] -eq $null) { } else { "`n" }
$output += $seperator + "[$section]";
foreach($key in $ini.$section.Keys)
{
if ( $key.StartsWith('Comment') )
{
$output += $ini.$section.$key
}
elseif ($ini.$section.$key -is [System.Collections.Specialized.OrderedDictionary]) {
foreach($subkey in $ini.$section.$key.Keys) {
if ($subkey -is [int]) {
$output += "$key[] = " + $ini.$section.$key.$subkey
} else {
$output += "$key[$subkey] = " + $ini.$section.$key.$subkey
}
}
}
else
{
$output += "$key = " + $ini.$section.$key
}
}
}
$output | Set-Content $filePath -Force
}
$ini = Parse-IniFile -filePath ".\config.ini"
Set-IniFile -ini $ini -filePath ".\config_copy.ini"
Write-Host "=first_section"
$ini["first_section"]
Write-Host "=second_section"
$ini["second_section"]
Write-Host "=second_section.second_section"
$ini["second_section"]["second_section"]
Write-Host "=third_section"
$ini["third_section"]
Write-Host "=third_section.phpversion"
$ini["third_section"]["phpversion"]
The output :
PS C:\Users\itesant> .\my_script.ps1
Name Value
---- -----
=readme
Comment1 ; This is a sample configuration file
Comment2 ; Comments start with ';', as in php.ini
=asset
Comment1 ; Allows you to select assets based on custom fields
environnement_field "Environnement"
environnement_values {0}
decommissioned_field "Deco"
decommissioned_values {0, 1, 2, 3}
=form
second_section {one, two, 0, 1...}
=form.second_section
one "1 associated"
two "2 associated"
three 3
four 4,4
0 "1 unassociated"
1 "2 unassociated"
2 1
3 2,2
=second_section
path "/usr/local/bin"
URL "http://www.example.com/~username"
second_section {one, two, three, four...}
=second_section.second_section
one "1 associated"
two "2 associated"
three 3
four 4,4
0 "1 unassociated"
1 "2 unassociated"
2 1
3 2,2
=third_section
phpversion {0, 1, 2, 3}
=third_section.phpversion
0 "5.0"
1 "5.1"
2 "5.2"
3 "5.3"
You can see some regex here :
Section: https://regex101.com/r/maYLKE/3
Comment: https://regex101.com/r/IE5FJH/2
Array Integer: https://regex101.com/r/AuuOi3/2
Array Decimal: https://regex101.com/r/Erjjym/2
Array Everything else: https://regex101.com/r/guC1Yd/2
Array associated Integer: https://regex101.com/r/Us56SL/2
Array associated Decimal: https://regex101.com/r/MrCZ9n/2
Array associated Everything else: https://regex101.com/r/TbYcyf/4