$theTitle=wp_title(" - ", false); if($theTitle != "") { ?>
About Virtualization, VDI, SBC, Application Compatibility and anything else I feel like
This morning Aaron Parker was wondering if Hash Tables could be used to work with ini files:
I thought it was a great idea because in Hash Tables you can use the . operator to get or set a Hash Table entry. But I wondered what to do with sections in ini files. Then I got the idea to use nested Hash Tables for that.
The result is two functions, one to read an ini file into a nested Hash Table and one function to write it back to an ini file.
Let’s look at the code (all code can be download with the link at the bottom of the page), starting with reading the ini file:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | function Get-IniFile { param ( [parameter(mandatory=$true, position=0, valuefrompipelinebypropertyname=$true, valuefrompipeline=$true)][string]$FilePath ) $ini = New-Object System.Collections.Specialized.OrderedDictionary $currentSection = New-Object System.Collections.Specialized.OrderedDictionary $curSectionName = "default" switch -regex (gc $FilePath) { "^\[(?<section>.*)\]" { $ini.Add($curSectionName, $currentSection) $curSectionName = $Matches['Section'] $currentSection = New-Object System.Collections.Specialized.OrderedDictionary } "(?<key>\w+)\=(?<value>\w+)" { # add to current section Hash Set $currentSection.Add($Matches['Key'], $Matches['Value']) } "^$" { # ignore blank line } "(?<key>\;)(?<value>.*)" { $currentSection.Add($Matches['Key'], $Matches['Value']) } default { throw "Unidentified: $_" # should not happen } } if ($ini.Keys -notcontains $curSectionName) { $ini.Add($curSectionName, $currentSection) } return $ini } |
Let’s take an example ini file, the notorious win.ini:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | ; for 16-bit app support [fonts] [extensions] [mci extensions] [files] [Mail] MAPI=1 CMCDLLNAME32=mapi32.dll CMC=1 MAPIX=1 MAPIXVER=1.0.0.1 OLEMessaging=1 [MCI Extensions.BAK] 3g2=MPEGVideo 3gp=MPEGVideo 3gp2=MPEGVideo 3gpp=MPEGVideo aac=MPEGVideo adt=MPEGVideo adts=MPEGVideo m2t=MPEGVideo m2ts=MPEGVideo m2v=MPEGVideo m4a=MPEGVideo m4v=MPEGVideo mod=MPEGVideo mov=MPEGVideo mp4=MPEGVideo mp4v=MPEGVideo mts=MPEGVideo ts=MPEGVideo tts=MPEGVideo |
And this is how to use it:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | # read ini file $ini = Get-Ini -FilePath "C:\Windows\win.ini" # change a value # note that sections can have spaces so we must embed those in double quotes # same for values because they can have digits in it. $ini."MCI Extensions.BAK"."3gp" = "blabla" # add a value to an existing session $ini."MCI Extensions.BAK".Add("foo", "bar") # create a section $ini.Add("PowerShell", (New-Object System.Collections.Specialized.OrderedDictionary)) # add a value to the new section $ini."PowerShell".Add("Coding", "Cool") |
But remember we did not write anything to disk, our Hash Tables exist only in memory. So we need a function to write the ini file back to disk:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | function Out-IniFile{ param ( [parameter(mandatory=$true, position=0, valuefrompipelinebypropertyname=$true, valuefrompipeline=$true)][System.Collections.Specialized.OrderedDictionary]$ini, [parameter(mandatory=$false,position=1, valuefrompipelinebypropertyname=$true, valuefrompipeline=$false)][String]$FilePath ) $output = "" ForEach ($section in $ini.GetEnumerator()) { if ($section.Name -ne "default") { # insert a blank line after a section $sep = @{$true="";$false="`r`n"}[[String]::IsNullOrWhiteSpace($output)] $output += "$sep[$($section.Name)]`r`n" } ForEach ($entry in $section.Value.GetEnumerator()) { $sep = @{$true="";$false="="}[$entry.Name -eq ";"] $output += "$($entry.Name)$sep$($entry.Value)`r`n" } } $output = $output.TrimEnd("`r`n") if ([String]::IsNullOrEmpty($FilePath)) { return $output } else { $output | Out-File -FilePath $FilePath -Encoding:ASCII } } |
And finally we can save the ini file back to disk:
1 2 | #write ini file $ini | Out-Ini -FilePath "C:\Windows\win.ini" |
2 Responses for "Handling ini files in PowerShell"
I did something very similar to that back in VBScript using Scripting.Dictionary objects. It’s a great approach for reading INI files, but I didn’t use it much for writing them, because of comment lines. Comments would either be deleted or placed back in the wrong order, which can be a pain.
In PowerShell, I mostly use XML files instead of INI, but on the occasions that I do use INI files, I rely on the Win32 API instead.
Hi Dave, yeah luckily ini files are becoming a part of history 😀
Win32 API handles INI files perfectly of course but might be a little difficult and do not really work PowerShell “style”.
Comments are handled in this PowerShell version btw.
Leave a reply