Giter Club home page Giter Club logo

psini's People

Contributors

colinbate avatar davidhayesbc avatar gitter-badger avatar ildar-shaimordanov avatar lipkau avatar michaelpf85 avatar popojargo avatar seanjseymour avatar severinleonhardt avatar tcartwright avatar waffle-iron avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

psini's Issues

Section order not preserved

I would expect the order of INI sections to match the order of the names in the hash:

$Category1 = @{“Key1”=”Value1”;”Key2”=”Value2”}
$Category2 = @{“Key1”=”Value1”;”Key2”=”Value2”}
$NewINIContent = @{“Category1”=$Category1;”Category2”=$Category2}
Out-IniFile -InputObject $NewINIContent -FilePath "C:\MyNewFile.ini"

Expected:

[Category1]
Key1=Value1
Key2=Value2

[Category2]
Key1=Value1
Key2=Value2

Actual:

[Category2]
Key1=Value1
Key2=Value2

[Category1]
Key1=Value1
Key2=Value2

Abstract away the concept of converting to/from INI format from that of reading/writing an INI file

This library would be much more useful to me if we were to separate the concept of converting to/from ini format from reading/writing files - since really these are separate things.

I propose that we create the functions ConvertTo-Ini and ConvertFrom-Ini, which would match the existing powershell api for JSON, Csv, etc.
Out-IniFile and Get-IniContent should then be rewritten to use these new functions.

Pros:

  • More flexible
  • More consistent with similar existing PS functionality

Cons:

  • ???

I need this ability for my project and am working on this.
I'm happy to consider any feedback and send a PR if you agree this would be useful

Submission: ConvertFrom-Ini and ConvertTo-Ini

I would make a pull request, but I'm not that well versed in working with git/github. Nevermind the fact there's a version mismatch with the latest tag and what's in the github psd1 file: #61

These are based on Get-IniContent and Out-IniFile functions from PsIni version 3.1.2 pulled from the public PSGallery. I actually need to work with a web sourced ini file, and prefer not to download it to the filesystem if I don't have to. The following functions do exactly what I need them to do and behave consistently with their reference function.

I know you have something you're working on for 4.0, but I'm a little impatient and will be using these submissions for my own needs in the meantime until you are comfortable releasing 4.0 to the gallery.

#example script
Import-Module PsIni
Import-Module HPCMSL
$SoftPaq = Get-SoftPaqList -Category DriverPack
$MetaData = Invoke-WebRequest -Uri "https://$($SoftPaq.MetaData)" | Select-Object -ExpandProperty Content
$IniData = $MetaData | ConvertFrom-Ini
$keys = $IniData.'System Information'.Keys.split()
$count = ($keys.count / 2)
$SystemInformation = 1..$count | Foreach-Object {
     if ($_.length -eq 1) {
          $index = "0$_"
     } else {
          $index = $_.ToString()
     }
     $System = $keys | Where-Object {$_ -like "*$Index"}
     [PSCustomObject]@{
          ProductCode = $IniData.'System Information'[$System[0]].split('x')[1]
          Models = $IniData.'System Information'[$System[1]].Split(',')
     }
}
Write-Output $SystemInformation

Example Output

ProductCode Models
----------- ------
870B        {HP ELITEDESK 800 G6 SMALL FORM FACTOR, HP ELITEDESK 800 G6 TOWER, HP ELITEDESK 880 G6 TOWER, HP Z1 G6 Entry Tower}
870C        {HP ELITEDESK 800 G6 SMALL FORM FACTOR, HP ELITEDESK 800 G6 TOWER, HP ELITEDESK 880 G6 TOWER, HP Z1 G6 Entry Tower}
8712        {HP PRODESK 600 G6 MICROTOWER}
8713        {HP PRODESK 600 G6 I MICROTOWER, HP PRODESK 680 G6 I MICROTOWER}
8717        {HP PRODESK 400 G7 MICROTOWER}
8718        {HP PRODESK 480 G7 I MICROTOWER}
Function ConvertFrom-Ini {
    <#
    .Synopsis
        Converts the content of a string (such as from Get-Content)

    .Description
        Converts the content of an INI formatted string object and returns it as a hashtable

    .Notes
        Author		: Oliver Lipkau <[email protected]>
        Blog            : http://oliver.lipkau.net/blog/
	Source		: https://github.com/lipkau/PsIni
         	          http://gallery.technet.microsoft.com/scriptcenter/ea40c1ef-c856-434b-b8fb-ebd7a76e8d91

        #Requires -Version 2.0

    .Inputs
        System.String

    .Outputs
        System.Collections.Specialized.OrderedDictionary

    .Example
        $IniFileContent = Get-Content "C:\myinifile.ini" | ConvertFrom-Ini
        -----------
        Description
        Saves the content of the c:\myinifile.ini in a hashtable called $FileContent

    .Example
        $IniContent = (Invoke-WebRequest -Uri $uri).Content | ConvertFrom-Ini
        -----------
        Description
        Gets the content of a web-sourced ini file passed through the pipe into a hashtable called $IniContent

    .Example
        C:\PS>$FileContent = Get-Content "c:\settings.ini" | ConvertFrom-Ini
        C:\PS>$FileContent["Section"]["Key"]
        -----------
        Description
        Returns the key "Key" of the section "Section" from the C:\settings.ini file

    .Link
	Get-IniContent
        Out-IniFile
	ConvertTo-Ini
    #>

    [CmdletBinding()]
    [OutputType(
        [System.Collections.Specialized.OrderedDictionary]
    )]
    Param(
        # Specifies the path to the input file.
        [ValidateNotNullOrEmpty()]
        [Parameter( Mandatory = $true, ValueFromPipeline = $true )]
        [String]
        $InputObject,

        # Specify what characters should be describe a comment.
        # Lines starting with the characters provided will be rendered as comments.
        # Default: ";"
        [Char[]]
        $CommentChar = @(';','#','/'),

        # Remove lines determined to be comments from the resulting dictionary.
        [Switch]
        $IgnoreComments
    )

    Begin {
        Write-Debug "PsBoundParameters:"
        $PSBoundParameters.GetEnumerator() | ForEach-Object { Write-Debug $_ }
        if ($PSBoundParameters['Debug']) {
            $DebugPreference = 'Continue'
        }
        Write-Debug "DebugPreference: $DebugPreference"

        Write-Verbose "$($MyInvocation.MyCommand.Name):: Function started"

        $commentRegex = "^\s*([$($CommentChar -join '')].*)$"
        $sectionRegex = "^\s*\[(.+)\]\s*$"
        $keyRegex     = "^\s*(.+?)\s*=\s*(['`"]?)(.*)\2\s*$"

        Write-Debug ("commentRegex is {0}." -f $commentRegex)
    }

    Process {
        Write-Verbose "$($MyInvocation.MyCommand.Name):: Processing [System.String]"

        $ini = New-Object System.Collections.Specialized.OrderedDictionary([System.StringComparer]::OrdinalIgnoreCase)
        #$ini = @{}


        $commentCount = 0
	foreach ($Line in $InputObject.Split([System.Environment]::NewLine)) {
        switch -regex ($Line) {
            $sectionRegex {
                # Section
                $section = $matches[1]
                Write-Verbose "$($MyInvocation.MyCommand.Name):: Adding section : $section"
                $ini[$section] = New-Object System.Collections.Specialized.OrderedDictionary([System.StringComparer]::OrdinalIgnoreCase)
                $CommentCount = 0
                continue
            }
            $commentRegex {
                # Comment
                if (!$IgnoreComments) {
                    if (!(test-path "variable:local:section")) {
                        $section = $script:NoSection
                        $ini[$section] = New-Object System.Collections.Specialized.OrderedDictionary([System.StringComparer]::OrdinalIgnoreCase)
                    }
                    $value = $matches[1]
                    $CommentCount++
                    Write-Debug ("Incremented CommentCount is now {0}." -f $CommentCount)
                    $name = "Comment" + $CommentCount
                    Write-Verbose "$($MyInvocation.MyCommand.Name):: Adding $name with value: $value"
                    $ini[$section][$name] = $value
                }
                else {
                    Write-Debug ("Ignoring comment {0}." -f $matches[1])
                }

                continue
            }
            $keyRegex {
                # Key
                if (!(test-path "variable:local:section")) {
                    $section = $script:NoSection
                    $ini[$section] = New-Object System.Collections.Specialized.OrderedDictionary([System.StringComparer]::OrdinalIgnoreCase)
                }
                $name, $value = $matches[1, 3]
                Write-Verbose "$($MyInvocation.MyCommand.Name):: Adding key $name with value: $value"
                if (-not $ini[$section][$name]) {
                    $ini[$section][$name] = $value
                }
                else {
                    if ($ini[$section][$name] -is [string]) {
                        $ini[$section][$name] = [System.Collections.ArrayList]::new()
                        $ini[$section][$name].Add($ini[$section][$name]) | Out-Null
                        $ini[$section][$name].Add($value) | Out-Null
                    }
                    else {
                        $ini[$section][$name].Add($value) | Out-Null
                    }
                }
                continue
            }
        }
	}
        Write-Verbose "$($MyInvocation.MyCommand.Name):: Finished Processing [System.String]"
        Write-Output $ini
    }

    End {
        Write-Verbose "$($MyInvocation.MyCommand.Name):: Function ended"
    }
}

Set-Alias cfi ConvertFrom-Ini
Function ConvertTo-Ini {
    <#
    .Synopsis
        Write hash content to INI string object

    .Description
        Write hash content to INI string object

    .Notes
        Author      : Oliver Lipkau <[email protected]>
        Blog        : http://oliver.lipkau.net/blog/
        Source      : https://github.com/lipkau/PsIni
                      http://gallery.technet.microsoft.com/scriptcenter/ea40c1ef-c856-434b-b8fb-ebd7a76e8d91

        #Requires -Version 2.0

    .Inputs
        System.String
        System.Collections.IDictionary

    .Outputs
        System.String

    .Example
        $IniVar | ConvertTo-Ini | Out-File "C:\myinifile.ini"
        -----------
        Description
        Saves the content of the $IniVar Hashtable to the INI File c:\myinifile.ini

    .Example
        $IniVar | ConvertTo-Ini | Out-File "C:\myinifile.ini" -Force
        -----------
        Description
        Saves the content of the $IniVar Hashtable to the INI File c:\myinifile.ini and overwrites the file if it is already present

    .Example
        $Category1 = @{“Key1”=”Value1”;”Key2”=”Value2”}
        $Category2 = @{“Key1”=”Value1”;”Key2”=”Value2”}
        $NewINIContent = @{“Category1”=$Category1;”Category2”=$Category2}
        ConvertTo-Ini -InputObject $NewINIContent | Out-File -FilePath "C:\MyNewFile.ini"
        -----------
        Description
        Creating a custom Hashtable and saving it to C:\MyNewFile.ini
    .Link
        Get-IniContent
	ConvertFrom-Ini
    #>

    [CmdletBinding()]
    [OutputType(
        [System.IO.FileSystemInfo]
    )]
    Param(
        # Specifies the Hashtable to be written to the file. Enter a variable that contains the objects or type a command or expression that gets the objects.
        [Parameter( Mandatory = $true, ValueFromPipeline = $true )]
        [System.Collections.IDictionary]
        $InputObject,

        # Adds spaces around the equal sign when writing the key = value
        [Switch]
        $Loose,

        # Writes the file as "pretty" as possible
        #
        # Adds an extra linebreak between Sections
        [Switch]
        $Pretty
    )

    Begin {
        Write-Debug "PsBoundParameters:"
        $PSBoundParameters.GetEnumerator() | ForEach-Object { Write-Debug $_ }
        if ($PSBoundParameters['Debug']) {
            $DebugPreference = 'Continue'
        }
        Write-Debug "DebugPreference: $DebugPreference"

        Write-Verbose "$($MyInvocation.MyCommand.Name):: Function started"

        function Out-Keys {
            param(
                [ValidateNotNullOrEmpty()]
                [Parameter( Mandatory, ValueFromPipeline )]
                [System.Collections.IDictionary]
                $InputObject,

                [Parameter( Mandatory )]
                $Delimiter,

                [Parameter( Mandatory )]
                $MyInvocation
            )

            Process {
                if (!($InputObject.get_keys())) {
                    Write-Warning ("No data found in '{0}'." -f $FilePath)
                }
		$outKey = @()
                Foreach ($key in $InputObject.get_keys()) {
                    if ($key -match "^Comment\d+") {
                        Write-Verbose "$($MyInvocation.MyCommand.Name):: Writing comment: $key"
                        $outKey += "$($InputObject[$key])"
                    }
                    else {
                        Write-Verbose "$($MyInvocation.MyCommand.Name):: Writing key: $key"
                        $InputObject[$key] |
                            ForEach-Object { $outKey +=  "$key$delimiter$_" }
                    }
                }
		Write-Output $outKey
            }
        }

        $delimiter = '='
        if ($Loose) {
            $delimiter = ' = '
        }

    }

    Process {
        $extraLF = ""

	Write-Debug ("Creating Output Object")
        $output = @()

        Write-Verbose "$($MyInvocation.MyCommand.Name):: Appending to object: [System.Object]"
        foreach ($i in $InputObject.get_keys()) {
            if (!($InputObject[$i].GetType().GetInterface('IDictionary'))) {
                #Key value pair
                Write-Verbose "$($MyInvocation.MyCommand.Name):: Writing key: $i"
                $output += "$i$delimiter$($InputObject[$i])"
            }
            elseif ($i -eq $script:NoSection) {
                #Key value pair of NoSection
                $output += Out-Keys $InputObject[$i] `
                    -Delimiter $delimiter `
                    -MyInvocation $MyInvocation
            }
            else {
                #Sections
                Write-Verbose "$($MyInvocation.MyCommand.Name):: Writing Section: [$i]"

                # Only write section, if it is not a dummy ($script:NoSection)
                if ($i -ne $script:NoSection) { $output += "$extraLF[$i]"}
                if ($Pretty) {
                    $extraLF = "`r`n"
                }

                if ( $InputObject[$i].Count) {
                    $output += Out-Keys $InputObject[$i] `
                        -Delimiter $delimiter `
                        -MyInvocation $MyInvocation
                }

            }
        }
        Write-Verbose "$($MyInvocation.MyCommand.Name):: Finished Writing to object: [System.Object]"
    }

    End {
        Write-Output $output
        Write-Verbose "$($MyInvocation.MyCommand.Name):: Function ended"
    }
}

Set-Alias cti ConvertTo-Ini

Please allow duplicate keys

For example, the php configuration file php.ini allows multiple same keys, such as extension=NAME1, extension=NAME2.

ValidatePattern generating an exception

I'm getting an error related to ValidatePattern associated with Out-Inifile's $FilePath parameter. For example:

Import-Module PsIni -Force

# place file in current directory
$file="$(GI '.')\settings.ini"

$Requestor = @{"Name"="John Doe";"Email"="[email protected]"}
$Analyst = @{"Name"="Jane Doe";"Email"="[email protected]"}
$NewINIContent = @{"Requestor"=$Requestor;"Analyst"=$Analyst}

Out-IniFile -InputObject $NewINIContent -FilePath $file

Produces this error:

Out-IniFile : Cannot validate argument on parameter 'FilePath'. The argument 
"C:\Users\cb2215\Documents\WindowsPowerShell\Scripts\Scaffolding\settings.ini" does not match the 
"^[a-zA-Z0-9öäüÜÖÄ!§$%&\(\)=;_\'+#\-\.,\`²³\{\[\]\}]{1,255}$" pattern. Supply an argument that matches 
"^[a-zA-Z0-9öäüÜÖÄ!§$%&\(\)=;_\'+#\-\.,\`²³\{\[\]\}]{1,255}$" and try the command again.
At line:10 char:51
+ Out-IniFile -InputObject $NewINIContent -FilePath $file
+                                                   ~~~~~
    + CategoryInfo          : InvalidData: (:) [Out-IniFile], ParameterBindingValidationException
    + FullyQualifiedErrorId : ParameterArgumentValidationError,Out-IniFile

Is there another way to do this? Is it needed? Maybe throwing an exception is the filepath is malformed would be better?

It would be nice to support the relative path, too:

Out-IniFile -InputObject $NewINIContent -FilePath '.\Settings.ini'

File with [ ] in path doesn't seem to work as expected.

Hello.

Writing a PS script and I seem to be unable to get it to load an ini file when the path to it contains [ ].

Ie, my PS script and the INI file lies in the path Z:\[Scripts]\[PS].

And my script:

$iniFile = ($PSScriptRoot + "\config.ini")
$ini = Get-IniContent $iniFile
$ini["main"]["debug"] = "false"
$ini | Out-IniFile -FilePath $iniFile -Force

I however get an error that the file wasn't found.

No files matching 'Z:\[Scripts]\[PS]\config.ini' were found.
At C:\Users\izz0j\Documents\WindowsPowerShell\Modules\PsIni\3.1.2\Functions\Get-IniContent.ps1:109 char:29
+         switch -regex -file $FilePath {
+                             ~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (Z:\[Scripts]\[PS]\config.ini:String) [], RuntimeException
    + FullyQualifiedErrorId : FileNotFound

It then fails to do modifications on the file, and $ini | Out-IniFile -FilePath $iniFile -Force successfully writes an empty file, since it's empty.

Quotes stripped and trimming

I used this code and compared the results:

Get-IniContent $iniPath\wincmd.ini | Out-IniFile $iniPath\wincmd2.ini

The difference, appart from white lines, were in the lines which had values that are within quotes:

key="some value"

This will become after output:

key=some value

This is a problem:

param7=""%n"" ==> param7="%n"

because standard ini reader will get n instead "n".

I noticed this because custom application I added in Total Commander lost quotes around parameter and consequetlly didn't work any more as expected.

Aligning behaviour of PSIni and Python's ConfigParser

Servus,

I'm working a lot with python and its parser for config files. There are several characteristics of an ini file structure, which are not considered in PSIni:

Multiline Values

Indented lines enable multiline values,

[Multiline Values]
chorus: I'm a lumberjack, and I'm okay
    I sleep all night and I work all day

which can be easily considered when insert the evaluation of a Regex-string ,e.g., ^( {4,}.*)\s*$, after inserting a key in Get-IniContent.

Interpolation of values

It enables values to contain format strings which refer to other values in the same section, or values in the special default section. Additionally extended interpolation enables to denote a value from a foreign section.

[BasicInterpolation]
home_dir = /Users
my_dir = %(home_dir)s/lumberjack

[ExtendedInterpolation]
my_pictures = ${BasicInterpolation:my_dir}/Pictures

Moreover, there is the oppurtunity to use environment variables as values.

I have implemented already these functionality but for the last published version in PSGallery 3.1.2. Is there the possibility to open a pull request for version 4.0?

Besides these extensions, there seems to be a misbehaviour when using redundant keys in one section. In Get-IniContent, the generation of [ArrayList] overwrites the current key and logically add to this key a empty object of [ArrayList]. I think, that behaviour is not intended and can be easily fixed.

I'm looking forward hearing from you!

Out-IniFile: UTF-8 without BOM

Hi Oliver! Thank you very much for your module - I love it - it is a great help to me!

I use PsIni in my project PSGPPreferences where I update GPT.INI in Group Policies with it. Unforunately recently I stumbled upon this bug: exchange12rocks/PSGPPreferences#28
I need to be able to write those ini-files in UTF-8, but without BOM, in a single transaction, i.e. I cannot remove BOM afterwards.

Currently Out-IniFile uses Out-File, which always writes BOM in Windows PowerShell, unless the user disables it globally. TBH I'd rather do not introduce global changes on my users' PCs.
Is there a particular reason Out-IniFile uses Out-File? Would it be possible to switch to Set-Content? As far as I am aware it supports UTF-8 w/o BOM.

White space being removed

Hi Oliver,
Thank you for your very useful modules.

I'm having one issue whereby the whitespace before the sections is being removed. I'm piping the get - set - out together. I'm using version 3.1.2.

Kind regards,

Ian

Writing a .ini file with an empty section

The issue occurs when I try to write an empty section. Here's a file parsed that I try to write back :

[emptysection]


[anothersection]
value = 1

The error that I received :

Out-Keys : Cannot validate argument on parameter 'InputObject'. The argument is null, empty, or an 
element of the argument collection contains a null value. Supply a collection that does not contain any 
null values and then try the command again.
At D:\Scripts\PowerShell modules\PsIni-master\Functions\Out-IniFile.ps1:219 char:26
+                 Out-Keys $InputObject[$i] `
+                          ~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidData: (:) [Out-Keys], ParameterBindingValidationException
    + FullyQualifiedErrorId : ParameterArgumentValidationError,Out-Keys

Any Idea what should be done here?

Writing new section and values?

I will admit, I am a novice with Powershell and such... I am trying to write out settings to an INI file, but there are times when there are sections and keys that are not in the INI file, and the output command generates an error that I am putting something into a null array.... Can anyone give me some advice on how to handle this?

Issue to keep all my Sections

Hi, i have a inifile like this :


[38923TECH11]	
Enabled=Y	

[38923FICHIER11]
Enabled=Y

[38923ULIS11]
Enabled=Y

But i use your function to parse it, and just echo the result of section + value of the key Enabled
i obtain it :

38923ULIS11  =  Y
38923FICHIER11  =  Y
No-Section  =  Y

Do you know why my first section [38923TECH11] is replaced my "no-section"?

No Section and Out-IniFile

In the Get-IniContent, you take care of .ini file with no [Section] of with keys not located in any section

if (!($section))  
                {  
                    $section = "No-Section"  
                    $ini[$section] = @{}  
                }  

But you don't take care of it in the Out-IniFile.
So if you have an ini file without any section (or with keys that are not in a section), you'll end up with a [No-Section] section, which technically is better but that is certainly not what you want:

Original INI file content

Test = OldValue

Update the file with something like this:

$FileContent = Get-IniContent "c:\ myinifile.ini"
$FileContent["No-Section"]["Test"] = "NewValue"
$FileContent | Out-IniFile "C:\myinifile.ini" –Force

Updated INI file content

[No-Section]
Test = NewValue

Importing the module

I am getting error:

Import-Module : File C:\Program Files\WindowsPowerShell\Modules\PsIni\3.1.3\PsIni.psm1 cannot be loaded because running scripts is disabled on
this system. For more information, see about_Execution_Policies at https:/go.microsoft.com/fwlink/?LinkID=135170.
At line:1 char:1
+ Import-Module PsIni
+ ~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : SecurityError: (:) [Import-Module], PSSecurityException
    + FullyQualifiedErrorId : UnauthorizedAccess,Microsoft.PowerShell.Commands.ImportModuleCommand
No

How do I set an execution policy for the session to allow for psini?

30 seconds for processing 5MB ini file

I have an ini file with size of 5MB.

When i try to do Get-IniContent file path it takes around 30 seconds.

is that normal?

is there any way to speed that up?

Can't get all section names from big ini file

I think that I found certain bug.
I have quite big ini file and when I trying to get names of all Sections I see Keys names of fist Section instead of it.

PS C:\Windows\system32> (Get-IniContent -FilePath 'C:\Users\admin\Desktop\indexes.txt').Keys

Name                           Value
----                           -----
coldPath                       $SPLUNK_DB\$_index_name\colddb
coldToFrozenDir
enableDataIntegrityControl     0
enableTsidxReduction           0
frozenTimePeriodInSecs         2678400
homePath                       $SPLUNK_DB\$_index_name\db
maxTotalDataSizeMB             512000
thawedPath                     $SPLUNK_DB\$_index_name\thaweddb
timePeriodInSecBeforeTsidxR...
tsidxReductionCheckPeriodInSec
repFactor                      auto

indexes.txt

If "=" missing from the ini file, it is ignored

Hi,

I was trying to use your module to update a nagios conf file, see below an example, and I found out if the "=" is missing (e.g. NRPEListener.dll, CheckWMI.dll) in the .ini file, it is going to be ignored with get-iniContent

Thanks for your module!

[modules]
NRPEListener.dll
NSClientListener.dll
CheckWMI.dll
FileLogger.dll
CheckSystem.dll
CheckDisk.dll
CheckEventLog.dll
CheckHelpers.dll
;# NSCLIENT++ MODULES
;# A list with DLLs to load at startup.
FileLogger.dll
CheckSystem.dll
CheckDisk.dll
NRPEListener.dll
;SysTray.dll
CheckEventLog.dll
CheckHelpers.dll
;CheckWMI.dll
CheckExternalScripts.dll
 
 
;# USE THIS FILE
;  Use the INI file as opposed to the registry if this is 0 and the use_reg in the registry is set to 1
;  the registry will be used instead.
use_file=1
 
[log]
;# LOG DEBUG
;  Set to 1 if you want debug message printed in the log file (debug messages are always printed to stdout when run with -test)
;debug=1
 
;# LOG FILE
;  The file to print log statements to
file=opsview-agent.log

Use [ordered] hashtables

Although it would bump compatibility requirements to Powershell v3.0, preserving the sections and keys order on round tripping is useful.
This requires a simple modification when initializing the hashtables:
$ini= [ordered] @{}
and
$ini[$section] = [ordered] @{}

If values are double in one section the Write operation add mistakes

For scripting VPN connections for MS Always On VPN we have to modify %AppData%\Microsoft\Network\Connections\PBK\rasphone.ini. This file contains multiple entries where values are double or even more often.
Before
DEVICE=vpn
DEVICE=vpn
After
DEVICE=System.Collections.ArrayList vpn
DEVICE=vpn
The first entry will always get this "System.Collections.ArrayList" at first.
This will come at every first entry where the same value appears multiple times.
I'm working with
$Content = Get-IniContent and
$NewContent | Out-IniFile ...
This behavoiur can be reproduced.

Preserve White Space

Would it be possible to preserve whitespace between sections?

For example, before the script touches an INI file, it could look like this:

[Category1]
Key1=Value1
Key2=Value2

[Category2]
Key1=Value1
Key2=Value2

[Category3]
Key1=Value1
Key2=Value2

[Category4]
Key1=Value1
Key2=Value2

After the script is done, all the white space is gone:

[Category1]
Key1=Value1
Key2=Value2
[Category2]
Key1=Value1
Key2=Value2
[Category3]
Key1=Value1
Key2=Value2
[Category4]
Key1=Value1
Key2=Value2

Makes readability more difficult after batch-editing a bunch of INIs.

Can't read Vmware Workstation .vmx

I am trying to read a Vmware .vmx file (Version 16 hardware)

So far no luck

here are the first few lines

.encoding = "windows-1252"
displayName = "Dev Master"
config.version = "8"
virtualHW.version = "18"
scsi0.present = "TRUE"
scsi0.virtualDev = "lsisas1068"
sata0.present = "TRUE"
memsize = "12288"

When I do a Write-Host ($file | Out-String)
I get this

Name                           Value
----                           -----
_                              {.encoding, displayName, config.version, virtualHW.version...}

Is the issue that .vmx files don't have sections?

Files edited with PsIni can't be read with GetPrivateProfileString

I'm having trouble getting any file edited with PsIni to have any value read using GetPrivateProfileString. GetPrivateProfileString always return the default value in the call. After this happens, even if I manually edit the ini file in something like notepad or ultraedit to revert back to the previous value, the calls to GetPrivateProfileString still can't read this value.

Create file at 'C:\Test.ini' and put in contents:

[Startup]
key=value

Update ini file:

$ini = Get-IniContent "C:\Test.ini"
$ini["Startup"]["key"] = "value_changed"
$ini | Out-IniFile -FilePath "C:\Test.ini" -Force

Demo script in linqpad:

[DllImport("kernel32")]
private static extern int GetPrivateProfileString(string section, string key, string def, StringBuilder retVal, int size, string filePath);

void Main()
{
	string path = @"C:\Test.ini";
	
	string value = ReadString(path, "Startup", "key", "");
	Console.WriteLine(value); // this is always ""
}

public string ReadString(string file, string section, string key, string defValue)
{
	StringBuilder stringBuilder = new StringBuilder(1024);
	GetPrivateProfileString(section, key, defValue, stringBuilder, 1024, file);
	return stringBuilder.ToString();
}

PSIni is not using semantic versioning after breaking change

Hello

I'm using PsIni 1.2.0 in my own module. Inside of my module, I'm calling Set-IniContent.
In my manifest I declare:

    RequiredModules        = @(
        @{
            ModuleName    = 'PsIni'; 
            ModuleVersion = '1.2.0'; 
        }
    )

In Version 1.2.0 the parameter NameValuePairs of Set-IniContent accepts a string. In Version 1.2.0.38 and later, NameValuePairs requires a hashtable. This is a breaking change of the public module api. Something that I not expect when only the revision/build-number changes. A breaking change should lead in to a new major version number (PSIni 2.0.0.0).

To put ModuleVersion = '1.2.0.0' in my module manifest does not work, because such a version number can not be found by PowerShell:

Import-Module : The required module 'PsIni' is not loaded. Load the module or remove the module from 'RequiredModules' in the file [...]

When I use ModuleVersion = '1.2.0' in the manifest, PowerShell may import version 1.2.0, 1.2.0.38 or 1.2.0.39 of PsIni (depending on what version is installed on the machine), because all of them are matching with 1.2.0. Not all systems where my module is used are under my control, I can't simply update all systems to PSini 1.2.0.39.

Now I have to do something like this:

# Note: PSIni 1.2.0 has the version 1.2.0.-1
if ((Get-Module -Name PsIni | Select-Object -ExpandProperty Version).Revision -le 0){
    # Do the call in 1.2.0 style
} else {
    # Do the call in >=1.2.0.38 style
}

This is very uncool, but it's a workaround. So it's not a big deal, but it would be nice when:

(based on Semantic Versioning 2.0.0)

  • Release Version 1.2.0.40 with the old public api
  • Then release 2.0.0.0 with the new public api

I then can put ModuleVersion = '1.2.0.40' or ModuleVersion = '2.0.0' in the manifest to force the sys admins to update to a specific version of PsIni (because the import of my module will fail). On this way, I can be sure which api I can expect.

Best regards
Elia

Installation of v3.1.2 fails

The installation of the latest version fails for me. See error below:

C:\Temp\> Install-Module PsIni -Force
WARNING: Source Location 'https://www.powershellgallery.com/api/v2/package/PsIni/3.1.2' is not valid.
PackageManagement\Install-Package : Package 'PsIni' failed to download.
At C:\Program Files\WindowsPowerShell\Modules\PowerShellGet\1.0.0.1\PSModule.psm1:1809 char:21
+ ...          $null = PackageManagement\Install-Package @PSBoundParameters
+                      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ResourceUnavailable: (C:\Users\mihirm...Ini\PsIni.nupkg:String) [Install-Package], Excep
   tion
    + FullyQualifiedErrorId : PackageFailedInstallOrDownload,Microsoft.PowerShell.PackageManagement.Cmdlets.InstallPac
   kage

Has anyone seen this before and/or know how to fix this?

Support of not well formatted INI files

Some INI file's formatting is not optimal.

Should we support any of these?

  • extra whitespaces before section
  • extra whitespaces after section
  • extra whitespaces around =
  • different comment char (supported since 19a413c)

Quotation marks that encapsulate values are truncated

I have noticed an issue that the Get-IniContent CmdLet does not maintain the fidelity of .INI/.INF values that are encapsulated within quotation marks. This issue will probably also affect values encapsulated win apostrophes as well.

For example, here is a file name Gpttmpl.inf that is used within Active Directory Group Policy Objects. The header of the file contains a single-entry key "signature" whose value is encapsulated in quotation marks ...

[Unicode]
Unicode=yes
[Version]
signature="$CHICAGO$"
Revision=1

When this file is updated via the Set-IniContent CmdLet (which calls Get-IniContent) the quotation marks are truncated resulting in ...

[Unicode]
Unicode=yes
[Version]
signature=$CHICAGO$
Revision=1

Under normal circumstances this would not be problem but some scenarios, such as when updating and importing a Group Policy Object, there is no tolerance for the loss of this fidelity.

I have tracked this change to the Get-IniContent CmdLet line 144 ...

$name, $value = $matches[1, 3]

The $value is populated with the results of the capture group 3 from the REGEX expression ...

$keyRegex     = "^\s*(.+?)\s*=\s*(['`"]?)(.*)\2\s*$"

Using that REGEX expression the following capture groups are returned ...

Capture Group 1 - signature
Capture Group 2 - "
Capture Group 3 - $CHICAGO$

Capture group 3 has the value $CHICAGO$ (no quotation marks). The result is that the quotation marks are not picked up and therefore not restored to the resulting output file.

To work around this issue temporarily I am using a different REGEX expression. The following REGEX expression which will extract the apostrophe and or quotation marks (if present) into capture group 2 and 4 respectively.

^\s*(.+?)\s*=\s*(['`"]?)(.*?)(['`"]?)$

for signature="$CHICAGO$" this results in ...

Capture Group 1 - signature
Capture Group 2 - "
Capture Group 3 - $CHICAGO$
Capture Group 4 - "

In addition I am adding some additional logic into lines 144-149 (inclusive) ...

if (($matches[2] -eq "`"") -and ($matches[4] -eq "`"")) {
    $matches[3] = "$($matches[2])$($matches[3])$($matches[4])"
}elseif (($matches[2] -eq "`'") -and ($matches[4] -eq "`'")) {
    $matches[3] = "$($matches[2])$($matches[3])$($matches[4])"
}

If capture groups 2 and 4 both have an apostrophe or quotation mark these will be restored to the value maintaining fidelity with the original value.

-IgnoreComments do not work

Hi,
I test the IgnoreComments, but it do not work

I test these:
Get-IniContent (-FilePath "C:\temp\abc.ini" -IgnoreComments)
Get-IniContent (-FilePath "C:\temp\abc.ini" -IgnoreComments $true)

Thanks :)

INI file that does not have a section

Is there a way for this to read a non-standard INI file that does not contain a section?

example:
Server="10.10.10.10"

I tried using:
$out = Get-IniContent "C:\ini.ini
$out["NoSection"]["Server"]

but that did not work. Any ideas? Thanks!

Wrong order in Out-IniFile

Out-IniFile retunrs wrong ordered category.

Example:
$Category1 = @{"Key1"="Value1";"Key2"="Value2"}
$Category2 = @{"Key1"="Value1";"Key2"="Value2"}
$NewINIContent = @{"Category1"=$Category1;"Category2"=$Category2}
Out-IniFile -InputObject $NewINIContent -FilePath "C:\temp\settings.ini"

Output content:
[Category2]
Key1=Value1
Key2=Value2
[Category1]
Key1=Value1
Key2=Value2

PsIni doesn't work with C:\ProgramData\Microsoft\Network\Connections\Pbk\rasphone.pbk

Not sure if you consider amending the scripts because the rasphone.pbk is a rather special INI file.
The issues are:

  • There are two Device entries per section, one is "Device" and one is "DEVICE" so it PsIni would need to be case sensitive optionally
  • There are multiple lines of "CustomAuthData" per section
  • When writing the file and selecting "Out-IniFile -FilePath $iniPath -Encoding UTF8" it will write the encoding in UTF-8-BOM instead of UTF-8 (not sure though if that is causing issues but I wanted to mention it)

When writing the file with Out-IniFile the mentioned wrong elements look like this - the (...) is added by me:

CustomAuthData=System.Collections.ArrayList D88F7E15760070006E002D0066007500740(...)
instead of
CustomAuthData=D88F7E15760070006E002D006(...)

or

DEVICE=System.Collections.ArrayList vpn
DEVICE=vpn
instead of
Device=WAN Miniport (SSTP)
DEVICE=vpn

Since this is a rather special INI file I'm not sure if it is worth looking into but I would be very happy to see it working since modifying the rasphone.pbk file is the only option to change certain settings for VPN connections in Windows.

Write initial content for wiki

Write content for wiki

  • Home
  • Get-IniContent
    • Alias
    • Parameters
    • Examples for parameters
  • Out-IniFile
    • Alias
    • Parameters
    • Examples for parameters
    • compatible with hashtable and [System.Collections.IDictionary]
  • Unit Tests

Get-IniContent doesn't work properly with Set-StrictMode -Version Latest

I've fixed this in my own code, body needs to be (see below)

[CmdletBinding()]
Param(
    [ValidateNotNullOrEmpty()]
    [ValidateScript({(Test-Path $_)})]
    [Parameter(ValueFromPipeline=$True,Mandatory=$True)]
    [string]$FilePath,
    [char[]]$CommentChar = @(";"),
    [switch]$IgnoreComments
)

Begin
{
    Write-Verbose "$($MyInvocation.MyCommand.Name):: Function started"
    $commentRegex = "^([$($CommentChar -join '')].*)$"
}

Process
{
    Write-Verbose "$($MyInvocation.MyCommand.Name):: Processing file: $Filepath"
    $commentCount =0
    $ini = New-Object System.Collections.Specialized.OrderedDictionary([System.StringComparer]::OrdinalIgnoreCase)
    switch -regex -file $FilePath
    {
        "^\[(.+)\]$" # Section
        {
            $section = $matches[1]
            $ini[$section] = New-Object System.Collections.Specialized.OrderedDictionary([System.StringComparer]::OrdinalIgnoreCase)
            $commentCount = 0
            continue
        }
        $commentRegex # Comment 
        {
            if (!$IgnoreComments)
            {
                if (!(test-path "variable:section"))
                {
                    $section = "_"
                    $ini[$section] = New-Object System.Collections.Specialized.OrderedDictionary([System.StringComparer]::OrdinalIgnoreCase)
                }
                $value = $matches[1]
                $commentCount++
                $name = "Comment" + $commentCount
                $ini[$section][$name] = $value
            }
            continue
        }  
        "(.+?)\s*=\s*(.*)" # Key
        {
            if (!(test-path "variable:section"))
            {
                $section = "_" 
                $ini[$section] = New-Object System.Collections.Specialized.OrderedDictionary([System.StringComparer]::OrdinalIgnoreCase)
            }
            $name,$value = $matches[1..2]
            $ini[$section][$name] = $value
            continue
        }
    }
    Write-Verbose "$($MyInvocation.MyCommand.Name):: Finished Processing file: $FilePath"
    Return $ini
}

End
    {Write-Verbose "$($MyInvocation.MyCommand.Name):: Function ended"}

}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.