INI file parsing in PowerShell

前端 未结 7 1152
[愿得一人]
[愿得一人] 2020-11-27 05:16

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 (         


        
相关标签:
7条回答
  • 2020-11-27 06:01

    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
    }
    
    0 讨论(0)
  • 2020-11-27 06:05

    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.

    0 讨论(0)
  • 2020-11-27 06:06

    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 
    
    0 讨论(0)
  • 2020-11-27 06:09

    I optimized this solution for my needs adding some things to the function and a new function for writing back the ini file:

    1. I made an ordered dictionary out of the original dictionary (hash table) to be able to preserve the file structure
    2. And to make it possible to preserve the comments and blank lines, I put them in a special key. They can be then ignored, when using data or thrown away when writing to a file as demonstrated below in the function Set-IniFile
    3. In 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
    ##
    
    0 讨论(0)
  • 2020-11-27 06:12

    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.

    0 讨论(0)
  • 2020-11-27 06:14

    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] = @{}.

    0 讨论(0)
提交回复
热议问题