Как создать элемент из файла XML с циклом, используя powershell? - PullRequest
2 голосов
/ 23 апреля 2020

Я собираюсь создать xml файл. Элемент xml, который я выбираю из файла .ini. У меня есть файл .ini больше 1. Я пробую это, но все еще могу создать только 1 элемент.

В моем файле .ini есть два типа файлов содержимого типа. 1-й.

[Product]
Name = NB A
String = Defaults and Exit

[Controller1]
Desc = Embedded Intel RAID

[Controller2]
Desc = Intel Optane Device

2-й.

[Product]
Name = NB A
String = Defaults and Exit

[Controller1]
Desc = Embedded SATA 

Мой ожидаемый вывод XML, будет 2 типа. 1-й.

<Product Name=NB A>
      <Controller controllertype="raid">
      <Controller controllertype="optane">
</Product>

2-й.

<Product Name=NB A>
      <Controller controllertype="raid">
</Product>

Код, который я написал до сих пор:

$ProductListXML = "D:\TST.XML"
$driver = "D:\ver.xml" 


# get an XMLTextWriter to create the XML
$XmlWriter = New-Object System.XMl.XmlTextWriter($ProductListXML,$Null)

# choose a pretty formatting:
$xmlWriter.Formatting = 'Indented'
$xmlWriter.Indentation = 1
$XmlWriter.IndentChar = "`t"

# write the header
$xmlWriter.WriteStartDocument()

$xmlWriter.WriteStartElement('ProductList')
$xmlWriter.WriteAttributeString('Version', '200422a')

# load it into an XML object:
$xml = New-Object -TypeName XML
$xml.Load($driver)
$FamilyCode = $Xml.drivergroup.family | Select-Object -ExpandProperty Code

foreach ($fc in $FamilyCode)
{
    $FCDesc = $Xml.drivergroup.family | Where-Object { $_.code -eq "$fc" }| Select-Object -ExpandProperty description
    $SystemID = $Xml.drivergroup.family |  Where-Object { $_.code -eq "$fc" } | Select-Object -ExpandProperty Product | Select-Object -ExpandProperty systemid
    $IDSplit = $SystemID -split "/"

    $XmlWriter.WriteStartElement('Family')   
    $XmlWriter.WriteAttributeString('code', "$fc")
    $XmlWriter.WriteAttributeString('decription', "$FCDesc")

    foreach($sid in $IDSplit)
    {

        $ID = Get-ChildItem -path "D:\product\*$sid*" | Select-Object -ExpandProperty BaseName

        foreach ($id in $ID)
        {
            $File = Get-ChildItem -path "D:\product\*$id*"
            $ReadINI = @{} 
            Get-Content "$File" | ForEach-Object {
            $_.Trim()
            } | Where-Object {

            $_ -notmatch '^(;|$)'
            } | ForEach-Object {
            if ($_ -match '^\[.*\]$') {
            $section = $_ -replace '\[|\]'
            $ReadINI[$section] = @{}
            } else {
            $key, $value = $_ -split '\s*=\s*', 2
            $ReadINI[$section][$key] = $value    
            }
            }

            $ProductName = $ReadINI["Product"]["Name"]
            $TechType = $ReadINI["Controller1"]["Node"]
            if($TechType -eq "Intel")
            {
                $TechType = "Intel"
            }
            else{

                $TechType = "AMD"
            }
            $FactoryDefaultsString = $ReadINI["Product"]["String"]

            $YearPath = "D:\*.txt"
            $YearMapping = Select-String -Path $YearPath -Pattern "$id" 
            if($YearMapping -like "*2017*")
            {
                $Year = "2017"
            }
            elseif($YearMapping -like "*2018*")
            {
                $Year = "2018"
            }
            elseif($YearMapping -like "*2019*")
            {
                $Year = "2019"
            }
            elseif($YearMapping -like "*2020*")
            {
                $Year = "2020"
            }
            elseif($YearMapping -like "*2021*")
            {
                $Year = "2021"
            }

            $XmlWriter.WriteStartElement('Product')
            $XmlWriter.WriteAttributeString('Name', "$ProductName")
            $XmlWriter.WriteAttributeString('SSID', "$id")
            $XmlWriter.WriteAttributeString('TechType', "$TechType")
            $XmlWriter.WriteAttributeString('Year', "$Year")
            $XmlWriter.WriteAttributeString('FactoryDefaultsString', "$FactoryDefaultsString")

            $Controller2 = Select-String -Path $File -Pattern "Controller2" | Select-Object -ExpandProperty Filename
            foreach ($cont2 in $Controller2)
            {
                $file = Get-ChildItem "D:\product\$cont2"
                $ReadTechTp = @{} 
                Get-Content "$file" | ForEach-Object {
                $_.Trim()
                } | Where-Object {

                $_ -notmatch '^(;|$)'
                } | ForEach-Object {
                if ($_ -match '^\[.*\]$') {
                $section = $_ -replace '\[|\]'
                $ReadTechTp[$section] = @{}
                } else {
                $key, $value = $_ -split '\s*=\s*', 2
                $ReadTechTp[$section][$key] = $value    
                }
                }

                $Desc = $ReadTechTp["Controller2"]["Desc"]

                if($Desc -like "*RAID*" -or $Controller1 -like "*SATA*")
                {
                    $ControllerType = "RAID"
                }
                else{
                    $ControllerType = "OPTANE"
                }

                $XmlWriter.WriteStartElement('Controller')
                $XmlWriter.WriteAttributeString('ControllerType', "$ControllerType")

            }


            $xmlWriter.WriteEndElement()

        }



    }


    $xmlWriter.WriteEndElement()
}

    $xmlWriter.WriteEndElement()

# finalize the document:
$xmlWriter.WriteEndDocument()
$xmlWriter.Flush()
$xmlWriter.Close()

Любой может помочь, пожалуйста. Советы и решения действительно ценятся. Спасибо

1 Ответ

1 голос
/ 23 апреля 2020

Вот как вы можете это сделать:

  • Создайте регулярное выражение для соответствия заголовкам. Это может быть что-то вроде ^\[(.+)\]$.

    • Соответствует началу строки с ^,
    • Находит первую [ скобку с \[,
    • Соответствует любому символу один или несколько раз в группа с (.+),
    • Соответствует последней скобке ] с \] и заканчивает строку с $.
  • Создайте регулярное выражение для сопоставления ключей и значений. Это может быть что-то вроде ^(.+?)\s*=\s*(.*)$.

    • Соответствует началу строки с ^,
    • Поиск ключей с любым символом один или неограниченное количество раз с (.+?),
    • Соответствует нулю или более пробел между знаком = с \s*=\s*
    • Совпадение нуля или более символов в группе с (.*) и конец строки с $.

Учитывая вышесказанное, мы можем сделать функцию, возвращающую файловую структуру .ini в виде вложенных таблиц ha sh. Я использую переключатели -File и -Regex из switch для чтения файла и поиска шаблонов регулярных выражений. Вы можете взглянуть на about_Switch для получения дополнительной информации.

Однако для этого, вероятно, потребуется дополнительная проверка ошибок, например, работа с комментариями и недействительными записями. Я только что сделал что-то, что будет работать для вышеуказанных .ini файлов.

function Get-IniFile {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory=$true)]
        [ValidateNotNullOrEmpty()]
        [string]$FilePath
    )

    $ini = [ordered]@{}

    switch -Regex -File $FilePath {

        # Match headers
        "^\[(.+)\]$" {
            $section = $Matches[1]
            $ini[$section] = [ordered]@{}
            continue
        }

        # Match key-value pairs
        "^(.+?)\s*=\s*(.*)$" {
            $name, $value = $matches[1..2]
            $ini[$section][$name] = $value
            continue
        }
    }

    return $ini
}

Затем вы можете перебрать два файла .ini, скажем, first.ini и second.ini. и создайте файлы XML для них обоих, используя таблицы ha sh, возвращаемые из Get-IniFile.

$iniFiles = "first.ini", "second.ini"

# Go through each .ini file
foreach ($file in $iniFiles) {

    # Create XML object to write to
    $xml = New-Object -TypeName System.Xml.XmlDocument

    # Get .ini file data into a hashtable
    $iniFileData = Get-IniFile -FilePath $file

    # Iterate first set of keys from hashtable
    foreach ($kvp in $iniFileData.GetEnumerator()) {

        # Create a product root key for the header
        $product = $xml.CreateElement($kvp.Key)

        # Iterate through key-value pairs
        foreach ($value in $kvp.Value.GetEnumerator()) {
            switch ($value.Name)
            {

                # If we found a name, this attribute belongs to the header
                "Name" {
                    $product.SetAttribute($value.Name, $value.Value)
                    $xml.AppendChild($product)
                    break
                }

                # Otherwise we found a descendent node
                "Desc" {

                    # Create descendent controller node
                    $controller = $xml.CreateElement("Controller")

                    # Determine how to set the attributes depending on the value
                    if ($value.Value -like "*RAID*" -or $value.Value -like "*SATA*") {
                        $controller.SetAttribute("controllertype", "raid");
                    } else {
                        $controller.SetAttribute("controllertype", "optane");
                    }

                    # Append controller node to product root node
                    $xml.Product.AppendChild($controller)
                    break
                }
            }
        }
    }

    # Save to XML file, using the name from the original file
    $filename = [System.IO.Path]::GetFileNameWithoutExtension($file)
    $xml.Save("{0}.xml" -f $filename)
}

первый. xml

<Product Name="NB A">
  <Controller controllertype="raid" />
  <Controller controllertype="optane" />
</Product>

второй. xml

<Product Name="NB A">
  <Controller controllertype="raid" />
</Product>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...