I\'m parsing simple (no sections) INI file in PowerShell. Here code I\'ve came up with, is there any way to simplify it?
convertfrom-stringdata -StringData (
This is really an extension to the current answer (couldn't seem to append a comment).
I messed around with this to do rudimentary handling of integers and decimals...
function Parse-IniFile ($file)
{
$ini = @{}
switch -regex -file $file
{
#Section.
"^\[(.+)\]$"
{
$section = $matches[1].Trim()
$ini[$section] = @{}
continue
}
#Int.
"^\s*([^#].+?)\s*=\s*(\d+)\s*$"
{
$name,$value = $matches[1..2]
$ini[$section][$name] = [int]$value
continue
}
#Decimal.
"^\s*([^#].+?)\s*=\s*(\d+\.\d+)\s*$"
{
$name,$value = $matches[1..2]
$ini[$section][$name] = [decimal]$value
continue
}
#Everything else.
"^\s*([^#].+?)\s*=\s*(.*)"
{
$name,$value = $matches[1..2]
$ini[$section][$name] = $value.Trim()
}
}
$ini
}
I'm not exactly sure what your source data looks like, or what your goal is. What exactly are you parsing for? Can you post a sample of the file? As-is, it looks like you're just concatenating carriage returns to the existing lines of the file and replacing \ with \.
Nor certain why you're using $_.ToString()
since $_
is already a string object output by Get-Content.
Is the goal just to take a file containing a bunch of name=value pairs, and convert that to a hashtable? That's what ConvertFrom-StringData
does, although that cmdlet is only available in the preview of PowerShell v2.
If your file looks like...
key1=value1
key2=value2
key3=value3
Then all you should need is
ConvertFrom-StringData (Get-Content .\deploy.ini)
I'm not sure I understand why you're tacking on extra carriage returns. There's also no need to use the -Begin
and -End
parameters, at least not as far as I can see from what you've posted.
nini looks like a library ... not sure for powershell
powershell crash
first step
[void][system.reflection.assembly]::loadfrom("nini.dll") (refer add-type now in ps2 )
after you can use it
$iniwr = new-object nini.config.iniconfigsource("...\ODBCINST.INI")
$iniwr.Configs et boom
I optimized this solution for my needs adding some things to the function and a new function for writing back the ini file:
Set-IniFile
Set-IniFile
using the options -PrintNoSection
and -PreserveNonData
, it can be controlled if NO_SECTION should be used and if the non-data lines (not matching key=value or [section] should be preserved.Function Get-IniFile ($file) # Based on "https://stackoverflow.com/a/422529"
{
$ini = [ordered]@{}
# Create a default section if none exist in the file. Like a java prop file.
$section = "NO_SECTION"
$ini[$section] = [ordered]@{}
switch -regex -file $file
{
"^\[(.+)\]$"
{
$section = $matches[1].Trim()
$ini[$section] = [ordered]@{}
}
"^\s*(.+?)\s*=\s*(.*)"
{
$name,$value = $matches[1..2]
$ini[$section][$name] = $value.Trim()
}
default
{
$ini[$section]["<$("{0:d4}" -f $CommentCount++)>"] = $_
}
}
$ini
}
Function Set-IniFile ($iniObject, $Path, $PrintNoSection=$false, $PreserveNonData=$true)
{ # Based on "http://www.out-web.net/?p=109"
$Content = @()
ForEach ($Category in $iniObject.Keys)
{
if ( ($Category -notlike 'NO_SECTION') -or $PrintNoSection )
{
# Put a newline before category as seperator, only if there is none
$seperator = if ($Content[$Content.Count - 1] -eq "") {} else { "`n" }
$Content += $seperator + "[$Category]";
}
ForEach ($Key in $iniObject.$Category.Keys)
{
if ( $Key.StartsWith('<') )
{
if ($PreserveNonData)
{
$Content += $iniObject.$Category.$Key
}
}
else
{
$Content += "$Key = " + $iniObject.$Category.$Key
}
}
}
$Content | Set-Content $Path -Force
}
### EXAMPLE
##
## $iniObj = Get-IniFile 'c:\myfile.ini'
##
## $iniObj.existingCategory1.exisitingKey = 'value0'
## $iniObj['newCategory'] = @{
## 'newKey1' = 'value1';
## 'newKey2' = 'value2'
## }
## $iniObj.existingCategory1.insert(0, 'keyAtFirstPlace', 'value3')
## $iniObj.remove('existingCategory2')
##
## Set-IniFile $iniObj 'c:\myNewfile.ini' -PreserveNonData $false
##
Don Jones almost had it right. Try:
ConvertFrom-StringData((Get-Content .\deploy.ini) -join "`n")
-join
converts the Object[] into a single string, with each item in the array separated by a newline char. ConvertFrom-StringData
then parses the string into key/value pairs.
After searching internet on this topic I've found a handful of solutions. All of them are hand parsing of file data so I gave up trying to make standard cmdlets to do the job. There are fancy solutions as this which support writing scenario.
There are simpler ones and as far as I need no writing support I've chose following very elegant code snippet:
Function Parse-IniFile ($file) {
$ini = @{}
# Create a default section if none exist in the file. Like a java prop file.
$section = "NO_SECTION"
$ini[$section] = @{}
switch -regex -file $file {
"^\[(.+)\]$" {
$section = $matches[1].Trim()
$ini[$section] = @{}
}
"^\s*([^#].+?)\s*=\s*(.*)" {
$name,$value = $matches[1..2]
# skip comments that start with semicolon:
if (!($name.StartsWith(";"))) {
$ini[$section][$name] = $value.Trim()
}
}
}
$ini
}
This one is Jacques Barathon's.
Update Thanks to Aasmund Eldhuset and @msorens for enhancements: whitespace trimming and comment support.
Update 2 Skip any name=value
pairs where name
starts with a semicolon ; which are comment lines. Replaced $ini [$section] = @{}
with $ini[$section] = @{}
.