Reading / parsing / writing .ini files in Powershell using XML
Last weekend I decided I needed to improve my Powershell scripting skills for a technical training I’m attending this week and started working on a script that could download, install and configure several applications unattended on demand. More on that script in a later blog post, for now I will focus on a specific feature I needed to script in order to make it all work.
One of the applications was still using a .ini file to configure some parameters of the tool and by nature of the script, I wanted to modify these values. One could use a search & replace method to change values in a .ini file (which is of course a simple text based file). In my search to easily parse a .ini file, I found several functions that could read the file and insert the values into hashtables. This makes values usable in a variable like $inihash[sectionname][keyname] = “value” however due to the nature of hashtables, it will sort the values in it’s hashtable by the hashvalue of it’s content. As most of us know, a hash value is different per object, per runtime, per system and will generate a unsorted mess. Inserting a .ini file with key1, key2, key3 might end up as key2, key1, key3 and there is no real way to circumvent this.
I was trying to work out the solution by working with hashtables, arrays and more .net objects in order to store the .ini values in a neat and sorted way. One could make a array with a hashtable with another nested array and hashtable but this would greatly increase complexity and reduce code friendliness as the $ini[section][key] notation would no longer function.
The other day I was casually discussing the problem with a fellow trainee and he suggested to (ab)use a XML object as a “storage box” as it would neatly allow nested constructions (section.key = value) and will remain sorted as it was imported (1,2,3 -> 1,2,3). First we would have to import a .ini file in to a XML object, later modify values and finally export the XML to a .ini file (sorted like the input .ini file).
Importing a .ini file to a [xml] object
Using the following function we can import a test.ini file and make a XML object. The function builds a string that we can later convert to XML (quick and dirty way to create custom XML objects in Powershell).
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Function Parse-IniFile ($file)
{
[String]$iniXMLString = '<?xml version="1.0" ?><ini>'
$opentag = ""
switch -regex -file $file
{
"^\[(.+)\]$"
{
if ($opentag -ne "") { $iniXMLString += "</"+$opentag +">" }
$section = $matches[1].Trim()
$iniXMLString += "<" + $section + ">"
$opentag = $section
}
"^\s*([^#].+?)\s*=\s*(.*)"
{
$name,$value = $matches[1..2]
$iniXMLString += "<" + $name + ">" + $value + "</" + $name + ">"
}
}
if ($tagopen -ne "") { $iniXMLString += "</"+$opentag+">" }
$iniXMLString += "</ini>"
Return $iniXMLString
}
Use the function with the following commands:
- powershell
1 |
|
This will create a XML object with all the values in the .ini file. This newly created object can be used to read the data from the .ini file in a easy manner:
- powershell
1 |
|
Using this [xml] object, you can easily read and manipulate data stored in your .ini file. When you are done processing your data, you’ll want to write a new .ini file. To achieve this, we would have to traverse the XML object and write the data in a .ini filetype using the following function:
- powershell
1 |
|
This will return a string with the content of your new .ini. All that’s left is to write back to the new content of the file:
- powershell
1 |
|
Should you wish to debug your XML object, try using the Format-XML function written by Keith Hill. A special thanks to my brother Floris for helping me with the recursive loop xml function. You can download the functions plus a little example in this zip file: Ini Powershell.