Powershell: найти группу текста в файле, затем извлечь определенную строку в этой группе текста

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

[Cluster1]
    GatewayIp=xx.xxx.xxx.xx
    IpAddress=xx.xxx.xxx.x
    MTU=0000
    NetMask=xxx.xxx.xxx.0
    Port=xxx
    Protocol=xxxx/xxxxx
    Sessions=xxxxxx
    Bands=xxx, xxx, x
    Binding=xxxxx
    GroupNumber=x
    InitQueue=xxxxxx
    Interface=xxxxxx
    Process=xxx
    SupportsCar=No
    SupportsCom=Yes
    SupportsPos=Yes
    SupportsXvd=No

[Cluster2]
    GatewayIp=xx.xxx.xxx.xx
    IpAddress=xx.xxx.xxx.x
    MTU=0000
    NetMask=xxx.xxx.xxx.0
    Port=xxx
    Protocol=xxxx/xxxxx
    Sessions=xxxxxx
    Bands=xxx, xxx, x
    Binding=xxxxx
    GroupNumber=x
    InitQueue=xxxxxx
    Interface=xxxxxx
    Process=xxx
    SupportsCar=No
    SupportsCom=No
    SupportsPos=No
    SupportsXvd=Yes

Я хочу извлечь «IpAddress» в разделе, где присутствуют эти строки:

    SupportsCom=Yes
    SupportsPos=Yes

Дело в том, что я пытался использовать -context для захвата n-й строки после имени раздела «[Cluster1]», но это имя раздела отличается от файла к файлу...

$ip = Select-String -Path "$location" -Pattern "\[Cluster1\]" -Context 0,2 |
    Foreach-Object {$_.Context.PostContext}

Я пытался использовать Precontext для захвата N-й строки перед SupportsCom=Yes, но позиция строки "IpAddress=" отличается от файла к файлу...

$ip = Select-String -Path "$location" -Pattern "    SupportsCom=Yes" -Context 14,0 |
    Foreach-Object { $_.Line,$_.Context.PreContext[0].Trim()}

Есть ли способ получить раздел, содержащий «SupportsCom=Yes», ​​зная, что раздел разделен пустой строкой выше и ниже, а затем найти в этом разделе строку, содержащую «IpAddress=», а затем вернуть значение после «=»?

Это формат INI. Используйте для этого специальный модуль вместо разбора простого текста, например Псини.

Theo 06.04.2022 12:42

На данный момент я не могу установить какие-либо модули, мне придется сделать запрос администратору AD, но ответ будет получен долго .... И окончательный скрипт предназначен для использования сотрудниками L1

Olbrok 06.04.2022 12:56
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
2
43
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Хорошо, поскольку вам не разрешено использовать модуль (возможно, позже ..), это должно дать вам то, что вы хотите.

# change the extension in the Filter to match that of your files
$configFiles = Get-ChildItem -Path 'X:\somewhere' -Filter '*.ini' -File

$result = foreach ($file in $configFiles) {
    # initialize these variables to $null
    $IpAddress = $supportsCom = $supportsPos = $null
    # loop through the file line by line and try regex matches on them
    switch -Regex -File $file {
        '^\[([^\]]+)]' { 
            # did we get all wanted entries from the previous cluster?
            if ($IpAddress -and $supportsCom -and $supportsPos) {
                if ($supportsCom -eq 'Yes' -and $supportsPos -eq 'Yes') {
                    # just output the IpAddress so it gets collected in variable $result
                    $IpAddress
                }
                # reset the variables to $null
                $IpAddress = $supportsCom = $supportsPos = $null
            }
            # start a new cluster
            $cluster = $matches[1]
        }
        '^\s+IpAddress\s*=\s*(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})' { $IpAddress = $matches[1]}
        '^\s+SupportsCom\s*=\s*(Yes|No)' { $supportsCom = $matches[1] }
        '^\s+SupportsPos\s*=\s*(Yes|No)' { $supportsPos = $matches[1]}
    }
}

# show results on screen
$result

# or save as text file
$result | Set-Content -Path 'X:\somewhere\IpAddresses.txt'

Хорошо, это работает, единственное, я просто хочу, чтобы IpAddress был выходным, а не кластером или именем файла, я помещу его в столбец csv вместе с другими данными, которые легче найти.

Olbrok 06.04.2022 15:04

@Olbrok Хорошо, я изменил это. Теперь он выводит только IP-адреса

Theo 06.04.2022 15:10

Обновленный ответ:

Если вас не волнует название раздела (разделов), в котором находится IpAddress, вы можете использовать этот «однострочный» (разбитый на несколько строк для удобства чтения):

$ip = (Get-Content $location -Raw) -split '\[.+?\]' | 
    ConvertFrom-StringData | 
    Where-Object { $_.SupportsCom -eq 'Yes' -and $_.SupportsPos -eq 'Yes' } | 
    ForEach-Object IpAddress 
  • Строка Get-Content считывает входной файл как одну многострочную строку и расколы в заголовках разделов (например, [Cluster1]).
  • ConvertFrom-StringData преобразует Key = Value строк в одну hashtable для каждого раздела.
  • Для каждой хеш-таблицы Where-Object проверяет, содержит ли она SupportsCom=Yes и SupportsPos=Yes
  • ForEach-Object IpAddress — это сокращение от Select-Object -ExpandProperty IpAddress, которое дает вам фактическое значение IpAddress вместо объекта, содержащего элемент с именем IpAddress.
  • Обратите внимание, что $ip может быть либо одним строковым значением, либо массивом строк (если есть несколько совпадающих разделов).

Оригинальный ответ:

Вы также можете написать функцию общего назначения, которая преобразует разделы INI в объекты. Это позволяет вам использовать конвейер с простым оператором Where-Object для получения интересующих вас данных.

Общая функция для вывода разделов INI в виде объектов один за другим:

Function Read-IniObjects {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory, ValueFromPipeline)] [String] $Path
    )

    process {
        $section = @{}   # A hashtable that stores all properties of the currently processed INI section.

        # Read file line by line and match each line by the given regular expressions.
        switch -File $Path -RegEx { 
            '^\s*\[(.+?)\]\s*$' {               # [SECTION]
                # Output all data of previous section
                if ( $section.Count ) { [PSCustomObject] $section }

                # Create new section data
                $section = [ordered] @{ IniSection = $matches[ 1 ] }
            }
            '^\s*(.+?)\s*=\s*(.+?)\s*$' {        # KEY = VALUE
                $key, $value = $matches[ 1..2 ]
                $section.$key = $value
            }
        }
        
        # Output all data of last section
        if ( $section.Count ) { [PSCustomObject] $section }
    }
}

Использование:

$ip = Read-IniObjects 'test.ini' |
    Where-Object { $_.SupportsCom -eq 'Yes' -and $_.SupportsPos -eq 'Yes' } | 
    ForEach-Object IpAddress

Примечания:

  • Файл INI анализируется с помощью оператора switch, который может напрямую использовать файл в качестве входных данных. Это намного быстрее, чем использование цикла Get-Content.
  • Поскольку мы используем параметр -RegEx, оператор switch сопоставляет каждую строку файла с заданными регулярными выражениями, вводя ветки case только в том случае, если текущая строка соответствует.
  • Получите подробное объяснение того, как работает RegEx:
    • сопоставьте строки, такие как [Section] -> RegEx101
    • сопоставьте строки, такие как Key = Value -> RegEx101
  • ForEach-Object IpAddress — это сокращение от Select-Object -ExpandProperty IpAddress, которое дает вам фактическое значение IpAddress вместо объекта, содержащего элемент с именем IpAddress.
  • Обратите внимание, что $ip может быть либо одним строковым значением, либо массивом строк (если есть несколько совпадающих разделов).

Другие вопросы по теме