Конкретный контент Powershell Get-Content внутри текста

Я получаю текстовый файл с несколькими списками, как показано ниже (отредактируйте: включен более точный пример набора данных)

# SYSTEM X
# SINGULAR
192.168.1.3
# SUB-SYSTEM V
192.168.1.4
192.168.1.5
192.168.1.6
# SYSTEM Y
# MANDATORY
192.168.1.7
192.168.1.8
192.168.1.9
192.168.1.7
192.168.1.8
192.168.1.9

Каждый "СИСТЕМНЫЙ комментарий" означает новый набор после него. Я хочу читать каждый блок контента отдельно, поэтому каждый набор должен быть назначен объекту, отбрасывающему встроенные комментарии. Мне просто нужны IP-адреса. Что-то вроде:

$ipX = get-content -path [file.txt] [set X]
$ipY = get-content -path [file.txt] [set Y]
$ipZ = get-content -path [file.txt] [set Z]

Но я не уверен, как на самом деле назначать эти наборы по отдельности. Помоги пожалуйста.

4 ответа

Решение

Вот одно из возможных решений. Результатом будет хэш-таблица, каждый ключ которой будет содержать любой массив IP-адресов для набора:

$result = @{}
get-content file.txt | foreach {
    if ($_ -match "#\s*SET\s+(\w+)") {
        $result[($key = $matches.1)] = @()
    }
    elseif ($_ -notlike "#*") {
        $result[$key] += $_
    }
}

Содержание $result:

Name                           Value                                                                                                                                                                                  
----                           -----                                                                                                                                                                                  
Y                              {[ip], [ip], [more ips]}                                                                                                                                                               
Z                              {[ip], [ip], [more ips]}                                                                                                                                                               
X                              {[ip], [ip], [more ips]}    

Вы можете использовать Select-String для извлечения определенного фрагмента текста:

# Update $section to be the set you want to target
$section = 'Set Y'
Get-Content a.txt -Raw |
    Select-String -Pattern "# $section.*\r?\n(?s)(.*?)(?=\r?\n# Set|$)" | Foreach-Object 
        {$_.Matches.Groups[1].Value}

С помощью Get-Content с участием -Rawчитает в файле как одну строку, что упрощает сопоставление нескольких строк. С PowerShell 7, Select-String включает -Raw переключатель, упрощающий этот процесс.

Это выводит результаты группы захвата 1, которые соответствуют (.*?). Если вы хотите захватить между комментариями, а не между Set <something> и Set <something>, вы можете редактировать -Pattern значение в конце только быть # скорее, чем # Set.

Разбивка регулярных выражений:

  • # соответствует персонажам # буквально
  • $section заменяет ваше значение переменной соответствует значению буквально при условии, что в строке нет символов регулярного выражения
  • .* соответствует любому символу (кроме терминаторов строк)
  • \r соответствует возврату каретки
  • ? Квантификатор - соответствует от нуля до единицы, столько раз, сколько возможно, отдача по мере необходимости (жадность)
  • \n соответствует символу перевода строки (новой строки)
  • (?s)модификатор: однострочный. Точка соответствует символам новой строки
  • 1-я группа захвата (.*?)
  • .*? лениво сопоставляет любые символы
  • Положительный взгляд вперед (?=\r?\n# Set)
  • \r? соответствует возврату каретки ноль или более раз
  • \n соответствует символу перевода строки (новой строки)
  • # Набор соответствует символам # Set буквально
  • $ соответствует концу строки

Вот еще один подход. Мы воспользуемся Foreach-Objectс -End блокировать [PSCustomObject] последний.

Get-Content $file | Foreach-Object {
    if($_ -match 'SET (.+?)'){
        if($ht){[PSCustomObject]$ht}
        $ht = [ordered]@{Set = $Matches.1}
    }
    if($_ -match '^[^#]'){
        $ht["IPs"] += $_
    }
} -End {if($ht){[PSCustomObject]$ht}}

Вывод

Set IPs               
--- ---               
X   [ip][ip][more ips]
Y   [ip][ip][more ips]
Z   [ip][ip][more ips]

Если вы хотите также обеспечить $ht пусто для начала, вы можете использовать -Begin блок.

Get-Content $file | Foreach-Object -Begin{$ht=$null}{
    if($_ -match 'SET (.+?)'){
        if($ht){[PSCustomObject]$ht}
        $ht = [ordered]@{Set = $Matches.1}
    }
    if($_ -match '^[^#]'){
        $ht["IPs"] += $_
    }
} -End {if($ht){[PSCustomObject]$ht}}

Если я правильно понял вопрос с новым примером, вы хотите проанализировать файл и создать отдельные переменные, каждая из которых содержит массив IP-адресов.

Если это так, вы можете сделать:

# loop through the file line-by-line
$result = switch -Regex -File 'D:\Test\thefile.txt' {
    '#\sSYSTEM\s(\w+)' {
        # start a new object, output the earlier object if available
        if ($obj) { $obj }
        $obj = [PsCustomObject]@{ 'System' = $Matches[1]; 'Ip' = @() }
    }
    '\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}' {
        # looks like an IPv4 address. Add it to the Ip property array of the object
        $obj.Ip += $_
    }
    default {}
}

Теперь у вас есть объекты массива ob в $ result:

System Ip                                                     
------ --                                                     
Y      {192.168.1.7, 192.168.1.8, 192.168.1.9, 192.168.1.7...}
X      {192.168.1.3, 192.168.1.4, 192.168.1.5, 192.168.1.6}  

Сделать отдельные переменные из этого так же просто, как:

$ipX = ($result | Where-Object { $_.System -eq 'X' }).Ip
$ipY = ($result | Where-Object { $_.System -eq 'Y' }).Ip
$ipZ = ($result | Where-Object { $_.System -eq 'Z' }).Ip

В вашем примере есть повторяющиеся IP-адреса. Если ты этого не хочешь, сделай
$ipX = ($result | Where-Object { $_.System -eq 'X' }).Ip | Select-Object -Unique (то же самое для других)

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