Конкретный контент 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
(то же самое для других)