Я занимаюсь этим уже несколько дней, я пытаюсь проанализировать несколько текстовых файлов, содержащих такие данные:
[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=», а затем вернуть значение после «=»?
На данный момент я не могу установить какие-либо модули, мне придется сделать запрос администратору AD, но ответ будет получен долго .... И окончательный скрипт предназначен для использования сотрудниками L1
Хорошо, поскольку вам не разрешено использовать модуль (возможно, позже ..), это должно дать вам то, что вы хотите.
# 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 Хорошо, я изменил это. Теперь он выводит только IP-адреса
Обновленный ответ:
Если вас не волнует название раздела (разделов), в котором находится 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
Примечания:
switch
, который может напрямую использовать файл в качестве входных данных. Это намного быстрее, чем использование цикла Get-Content
.-RegEx
, оператор switch сопоставляет каждую строку файла с заданными регулярными выражениями, вводя ветки case только в том случае, если текущая строка соответствует.ForEach-Object IpAddress
— это сокращение от Select-Object -ExpandProperty IpAddress
, которое дает вам фактическое значение IpAddress
вместо объекта, содержащего элемент с именем IpAddress
.$ip
может быть либо одним строковым значением, либо массивом строк (если есть несколько совпадающих разделов).
Это формат INI. Используйте для этого специальный модуль вместо разбора простого текста, например Псини.