Синтаксический анализ INI-файлов в PowerShell
Я разбираю простой (без разделов) INI-файл в PowerShell. Вот код, который я придумал, есть ли способ его упростить?
convertfrom-stringdata -StringData ( `
get-content .deploy.ini `
| foreach-object `
-Begin { $total = "" } `
{ $total += "`n" + $_.ToString() } `
-End { $total } `
).Replace("", "\")
7 ответов:
После поиска в интернете по этой теме я нашел несколько решений. Все они являются ручным синтаксическим анализом данных файлов, поэтому я отказался от попыток сделать стандартные командлеты для выполнения этой работы. Существуют причудливые решения, такие как 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 }Это Жак Баратон .
Обновление благодаря Aasmund Eldhuset и @msorens для улучшений: обрезка пробелов и поддержка комментариев.
Update 2 пропустить любые пары
name=value, гдеnameначинается с точки с запятой;, которая является строкой комментария . Заменил$ini [$section] = @{}на$ini[$section] = @{}.
Дон Джонс почти угадал. Попробуйте:
ConvertFrom-StringData((Get-Content .\deploy.ini) -join "`n")
-joinпреобразует объект [] в одну строку, при этом каждый элемент массива разделяется символом новой строки.ConvertFrom-StringDataзатем разбирает строку на пары ключ / значение.
Это действительно расширение к текущему ответу (не смог добавить комментарий).
Я возился с этим, чтобы сделать элементарную обработку целых и десятичных чисел...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 }
Одна из возможностей заключается в использовании библиотеки .NET ini. Нини например.
Я перевел Простой пример из документов Nini в PowerShell ниже. Тебе нужно посадить Нини.dll в том же каталоге, что и скрипт.
$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")
Я оптимизировал это решение для моих нужд добавив некоторые вещи в функцию и новую функцию для записи обратно ini-файла:
- я сделал упорядоченный словарь из исходного словаря (хэш-таблицы), чтобы сохранить структуру файла
И чтобы сохранить комментарии и пустые строки, я помещаю их в специальный ключ. Затем их можно игнорировать при использовании данных или выбрасывать при записи в файл, как показано ниже в функцииSet-IniFile- в
Set-IniFile, используя опции-PrintNoSectionи-PreserveNonData, можно управлять, следует ли использовать NO_SECTION и следует ли сохранять строки без данных (не совпадающие ключ=значение или [раздел]).
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 ##
Я не совсем уверен, как выглядят ваши исходные данные или какова ваша цель. Что именно вы анализируете? Можете ли вы опубликовать образец файла? Как есть, похоже, что вы просто объединяете возврат каретки в существующие строки файла и заменяете \ на \.
Ни точно, почему вы используете
$_.ToString(), так как$_уже является строковым объектом, выводимым Get-Content.Является ли целью просто взять файл, содержащий кучу пар имя=значение, и преобразовать его в хеш-таблица? Это то, что делает
ConvertFrom-StringData, хотя этот командлет доступен только в предварительном просмотре PowerShell v2.Если ваш файл выглядит так...
key1=value1 key2=value2 key3=value3Тогда все, что вам нужно, это
ConvertFrom-StringData (Get-Content .\deploy.ini)Я не уверен, что понимаю, почему вы привязываете дополнительные возвраты экипажей. Также нет необходимости использовать параметры
-Beginи-End, по крайней мере, насколько я могу судить из того, что вы опубликовали.
Нини выглядит как библиотека ... не уверен для powershell
Сбой Powershell
Первый Шаг
[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
Comments