Monday, 3 August 2015

Modifying an INI File

Occasionally, you might find yourself needing to modify a file which has an ini-type format, i.e. setting=value.  For example, you might be doing an unattended install of SQL using a script which needs to modify certain values in the configuration file.  Or perhaps you’re installing Micros-Retail’s XStore product which makes extensive use of files that use this format.

There are two methods I commonly use to do this type of thing, and which method I choose depends on how many values I’m modifying.  If it’s just one value, I’ll use the one-liner method but if I’m modifying many values I use a custom function I wrote to do this.  For the purposes of this post, let’s assume we’re customizing a SQL install and want to vary the instance name with each install.

The One-Liner Method

001
002
003
004

$iniFile = 'C:\Temp\SQLInstall.ini'
(Get-Content $iniFile) | foreach {
$_ -replace "INSTANCENAME=.+","INSTANCENAME=MYINSTANCE"} | Set-Content 
$iniFile

Pretty straightforward – any line matching “INSTANCENAME=” will be replaced with “INSTANCENAME=MYINSTANCE”.  While this approach works well for replacing one value, I like to move this work to a function when I’m replacing multiple values.  So let’s expand our scenario to include some other values that we want to modify and see how it looks with a function doing the work.

Using a Function

The basic approach with this function is to create a hashtable of the values we want to replace in the file and then pass the hashtable to a function which then does a search and replace and updates the file.  Let’s take a look at how it all works.

Since the function is expecting to receive a hashtable, let’s start by creating the input it’s expecting and then take a look at the function. 

001
002
003
004
005
006
007
008
009
010
011
012
013

$iniFile = 'C:\Temp\SQLInstall.ini'

$sqlInstallParams =
 @{
    "SQLSVCACCOUNT=" = "SQLServiceAccount"

    "INSTANCENAME=" = "MYINSTANCE";
    "SQLBACKUPDIR=" = "C:\Backups";
    "SQLUSERDBDIR=" = "C:\SQL\DB";
    "SQLUSERDBLOGDIR=" = "C:\SQL\DB\Logs";
    "SQLTEMPDBDIR=" = "C:\SQL\TempDB"
;
}


#Now call custom function to update the config file using the hashtable values
Set-IniValue –
targetFile $iniFile hashTable$sqlInstallParams

Nothing much to this step – we just create a basic hashtable with the values we want to replace.  What I like about this approach is that it allows you to see what’s being modified in an easy-to-read format.  Now let’s take a look at the function we just called in Step 1.

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030

function Set-IniValue ([string]$targetFile, [hashtable]$hashTable)
{

    # Get contents of current target file
    $content = Get-Content $targetFile

    # Loop through hash table of new values and compare each line of
    # file with each key from hash table

    # If no match is found, add line to text file
    $hashTable.GetEnumerator() | ForEach-Object {
        if (!(Select-String -Pattern $_.Key -Path $targetFile -Quiet
))
        {

            $newText = $_.Key + $_.Value
            Add-Content $targetFile $newText
        }
        else
        {
            foreach ($line in $content
)
            {

                #If line is found, replace it with key and new value
                if ($line -match $_.
key)
                {

                    $newText = $_.Key + $_.Value
                    [IO.File]::ReadAllText($targetFile).Replace($line,$newText
) `
                      | Set-Content $targetFile -Force
                }
            }
        }
    }
}

The function starts off by checking if any of the entries we want to modify don’t exist in the ini file.  If any are found which don’t exist, they are added.  It then checks for any matches with the current key of the hashtable and modifies that line with the corresponding value from the hashtable, saving the ini file after each change.  And that’s pretty much it – I’ve used this function thousands of times over the years and it has never let me down!

Cleanup

Set-Content adds a blank line to the end of the file each time it is run so if you’re modifying a few values you can end up with a number of blank lines at the end of the file.  If you want to clean this up, you can add this line after the function call to remove these blank lines.

001
(Get-Content $iniFile) | where {$_.Trim() -ne "" } | Set-Content $iniFile

No comments:

Post a Comment