Ini File IconThis morning Aaron Parker was wondering if Hash Tables could be used to work with ini files:

Aaron Parker | @stealthpuppy | @remkoweijnen @msh_dave need to see if this approach works for editing 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:

Get-IniFile' powershell Download
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:

win.ini ini Download
; 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:

Example powershell Download
# 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:

Out-IniFile powershell Download
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:

Save back to disk powershell Download
#write ini file
$ini | Out-Ini -FilePath "C:\Windows\win.ini"

Download PowerShell Script