Инициализация файла INI в PowerShell

Я разбираю простой (без разделов) INI файл в PowerShell. Вот код, который я придумал, есть ли способ его упростить?

convertfrom-stringdata -StringData ( `
  get-content .\deploy.ini `
  | foreach-object `
    -Begin { $total = "" }  `
    { $total += "`n" + $_.ToString() } `
    -End { $total } `
).Replace("\", "\\")

Ответы

Ответ 1

После поиска в Интернете по этой теме я нашел несколько решений. Все они представляют собой ручной анализ файлов, поэтому я отказался от попыток сделать стандартные командлеты для выполнения этой задачи. Есть причудливые решения, такие как this, которые поддерживают сценарий написания.

Есть более простые, и насколько мне не нужна поддержка записи, я выбрал следующий очень элегантный фрагмент кода:

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
}

Этот Jacques Barathon.

Обновление Благодаря Aasmund Eldhuset и @msorens для улучшений: обрезка пробелов и поддержка комментариев.

Обновить 2 Пропустить любые пары name=value, где name начинается с точки с запятой ;, которая представляет собой строки комментариев. Замените $ini [$section] = @{} на $ini[$section] = @{}.

Ответ 2

Дон Джонс почти все понял. Попробуйте:
ConvertFrom-StringData((Get-Content .\deploy.ini) -join "`n")

-join преобразует Object [] в одну строку, причем каждый элемент массива разделяется символом новой строки char. ConvertFrom-StringData затем анализирует строку в пары ключ/значение.

Ответ 3

Это действительно расширение для текущего ответа (похоже, не добавляет комментарий).

Я испортил это, чтобы выполнить рудиментарную обработку целых чисел и десятичных знаков...

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
}

Ответ 4

Одна из возможностей - использовать библиотеку .NET ini. Nini, например.

Я перевел Простой пример из документов Nini в PowerShell ниже. Вы должны поместить nini.dll в тот же каталог, что и script.

$scriptDir = Split-Path -parent $MyInvocation.MyCommand.Definition
Add-Type -path $scriptDir\nini.dll

$source = New-Object Nini.Config.IniConfigSource("e:\scratch\MyApp.ini")

$fileName = $source.Configs["Logging"].Get("File Name")
$columns = $source.Configs["Logging"].GetInt("MessageColumns")
$fileSize = $source.Configs["Logging"].GetLong("MaxFileSize")

Ответ 5

Я не совсем уверен, как выглядят ваши исходные данные или какая у вас цель. Для чего вы разбираетесь? Можете ли вы опубликовать образец файла? Как есть, похоже, что вы просто конкатенируете возврат каретки к существующим строкам файла и заменяете\на \.

Непонятно, почему вы используете $_.ToString(), поскольку $_ уже является строковым объектом, выводимым Get-Content.

Является ли цель просто взять файл, содержащий пару пар name = value, и преобразовать его в хеш-таблицу? То, что делает ConvertFrom-StringData, хотя этот командлет доступен только в предварительном просмотре PowerShell v2.

Если ваш файл выглядит как...

key1=value1
key2=value2
key3=value3

Тогда вам понадобится

ConvertFrom-StringData (Get-Content .\deploy.ini)

Я не уверен, что понимаю, почему вы используете дополнительные возвраты каретки. Также нет необходимости использовать параметры -Begin и -End, по крайней мере, не настолько, насколько я могу видеть из того, что вы опубликовали.

Ответ 6

Я оптимизировал это решение для моих нужд, добавляя некоторые функции к функции и новую функцию для записи ini файла:

  • Я сделал упорядоченный словарь из исходного словаря (хеш-таблицы), чтобы иметь возможность сохранить структуру файла
  • И чтобы сохранить комментарии и пустые строки, я помещаю их в специальный ключ. Затем они могут игнорироваться при использовании данных или отбрасываться при записи в файл, как показано ниже в функции Set-IniFile
  • В Set-IniFile с использованием опций -PrintNoSection и -PreserveNonData его можно контролировать, если NO_SECTION следует использовать, и если необходимо сохранить строки без данных (не соответствующие key = value или [section].

Function Get-IniFile ($file)       # Based on "/questions/388341/ini-file-parsing-in-powershell/1769169#1769169"
 {
    $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
##

Ответ 7

nini выглядит как библиотека... не уверен в powershell

сбой powerhell

первый шаг

[void][system.reflection.assembly]::loadfrom("nini.dll") (refer add-type now in ps2 )

после его использования

$iniwr = new-object nini.config.iniconfigsource("...\ODBCINST.INI") 

$iniwr.Configs et boom