Reading / parsing / writing .ini files in Powershell using XML

February 17, 2012 – 14:59

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).

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:

$iniXMLString = Parse-IniFile ".\Test.ini"
[xml]$iniXML = $iniXMLString

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:

Write-Host $iniXML.Section1.Key1
$iniXML.Section2.Key2 = "my new value"

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:

Function Loop-XML($node, $first) {
 $returnValue = ""
 if ($node.HasChildNodes) {
  if ($node.FirstChild.Name -eq "#text") {
   $returnValue += ($node.name + "=" + $node.FirstChild.value) + "`r`n"
  } else {
   if (!$first) {
    $returnValue +=  ("[" + $node.name + "]")  + "`r`n"
   }
   foreach($item in $node.ChildNodes) {
     $returnValue +=  (Loop-XML $item $FALSE)
   }
  }
 }
 Return $returnValue
}

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:

$newIni = Loop-XML $iniXML.ini $true
Set-Content ".\myNewFile.ini" $newIni

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.

  1. 3 Responses to “Reading / parsing / writing .ini files in Powershell using XML”

  2. I’m try to get this to work.
    But how could I add a extra entry to the XML object?

    By Geert on Jun 6, 2012

  3. I get no text to new ini file

    By Mike on Jul 2, 2012

  4. Thanks for your suggestions. I got your example to work.
    1.Question: How can i write the output of my XML-Object directly in an xml-file ?
    2.Question: I want to read in an ini-file convert it to an XML object in order to use XML to filter out the following occurances with a &combined match criteria.
    Match Criteria:
    1. Find all the keys called Arguments= that have identical values combined in addition
    2. all the keys called UserGroups= that have identical values.
    If this combined match criteria comes true, than rewrite to the new inifile with only one section where these match case come true.
    This has the impact that all other sections that matches the criteria are deleted.

    Example INI-File Match Criteria
    [SASIMAP]
    FileName=C:\Program Files (x86)\VBScript\CScript.exe
    Arguments=”C:\Windows\Application Compatibility Scripts\Logon\sasimap.vbs” //B //NoLogo //T:60
    UserGroups=Domainname\gl_w_SASIMAP
    Description=SASIMAP
    Wait=0
    WindowStyle=1
    [SASOLAP]
    FileName=C:\Program Files (x86)\VBScript\CScript.exe
    Arguments=”C:\Windows\Application Compatibility Scripts\Logon\sasimap.vbs” //B //NoLogo //T:60
    UserGroups=Domainname\gl_w_SASIMAP
    Description=SASOLAP
    Wait=0
    WindowStyle=1

    By Gottfried on Jul 21, 2015

Post a Comment