lipkau / psini Goto Github PK
View Code? Open in Web Editor NEWWork with INI files in PowerShell using hashtables
Home Page: http://lipkau.github.io/PsIni
License: MIT License
Work with INI files in PowerShell using hashtables
Home Page: http://lipkau.github.io/PsIni
License: MIT License
Hi,
like described by Microsoft, in the winpeshl.ini there should be keys without a value.
Get-IniContent are ignore that lines and i also can´t add such keys.
regrads
Frank
That would let PsIni handle files such as .git/config that use the .ini format without the extension.
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
Some INI files seem to use Windows-1252 encoding (e.g. mdash as x97), which is not a support output encoding.
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:
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
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
Because of lack of "=$true" in parameters these functions do not work with powershell 2.0
Use Semantic Versioning to track changes to library. Version 1.2 (currently) would become 1.2.0.
For example, the php configuration file php.ini
allows multiple same keys, such as extension=NAME1
, extension=NAME2
.
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'
Add unit tests (using Pester).
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.
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.
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:
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
.
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!
The following private functions need Unit Tests:
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.
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
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?
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?
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"?
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
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?
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?
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
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
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] @{}
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.
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.
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?
I tried to save — in my ini-file, but it got saved as UTF-8 with BOM, where I would like it saved as ANSI.
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();
}
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)
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
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?
Take the necessary measure to host the module in the chocolatey repo.
Necessary measure:
automate the update of the manifest (*.psd1)
automate the deployment of a release to github. includes
automate the deployment of a release to Powershell Gallery
automate the deployment of a release to chocolatey. includes:
Some INI file's formatting is not optimal.
Should we support any of these?
=
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.
It would be great if you add Chocolatey package.
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 :)
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!
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
declare the [OutputType()] of each function
Not sure if you consider amending the scripts because the rasphone.pbk is a rather special INI file.
The issues are:
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.
I added PsInI to PsGet's package list. Awaiting pull request.
Created this issue to document the enhancement.
Latest tag v3.1.2 is pointing to version 3.1.0, at least according to PsIni.psd1.
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"}
}
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.