Powershell:在文件中查找一组文本,然后提取该组文本中的特定行
Powershell : Find a group of text in a file then extract a specific line in that group of text
我已经研究了几天了,我正在尝试解析包含如下数据的多个文本文件:
[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 来获取节名“[Cluster1]”之后的第 n 行,但是该节名因文件而异...
$ip = Select-String -Path "$location" -Pattern "\[Cluster1\]" -Context 0,2 |
Foreach-Object {$_.Context.PostContext}
我试过在 SupportsCom=Yes 之前使用 Precontext 抓取第 N 行,但是“IpAddress=”的行位置因文件而异...
$ip = Select-String -Path "$location" -Pattern " SupportsCom=Yes" -Context 14,0 |
Foreach-Object { $_.Line,$_.Context.PreContext[0].Trim()}
有没有办法获取包含“SupportsCom=Yes”的部分,知道该部分由上下空行分隔,然后在该部分中搜索包含“IpAddress=”的字符串,然后 return “=”后面的值 ?
好的,因为不允许您使用模块(也许以后......),这应该可以满足您的需求
# 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
所在部分的名称,您可以使用此“one-liner”(为了便于阅读,分成多行) :
$ip = (Get-Content $location -Raw) -split '\[.+?\]' |
ConvertFrom-StringData |
Where-Object { $_.SupportsCom -eq 'Yes' -and $_.SupportsPos -eq 'Yes' } |
ForEach-Object IpAddress
Get-Content
行将输入文件作为单个 multi-line 字符串读取,并且 splits 它位于 headers 部分(例如 [Cluster1]
)。
ConvertFrom-StringData
将 Key = Value
行转换为每节 hashtable
行。
- 对于每个哈希表,
Where-Object
检查它是否包含 SupportsCom=Yes
和 SupportsPos=Yes
ForEach-Object IpAddress
是 shorthand 用于编写 Select-Object -ExpandProperty IpAddress
它为您提供 IpAddress
的实际值而不是包含名为 [= 的成员的 object 13=].
- 请注意,
$ip
可以是单个字符串值或字符串数组(如果有多个匹配部分)。
原回答:
您还可以编写一个 general-purpose 函数,将 INI 部分转换为 objects。这使您能够通过简单的 Where-Object
语句使用管道来获取您感兴趣的数据。
将 INI 部分输出为 objects 的通用函数,一个接一个:
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 工作原理的详细说明:
ForEach-Object IpAddress
是 shorthand 用于编写 Select-Object -ExpandProperty IpAddress
它为您提供 IpAddress
的实际值而不是包含名为 [= 的成员的 object 13=].
- 请注意,
$ip
可以是单个字符串值或字符串数组(如果有多个匹配部分)。
我已经研究了几天了,我正在尝试解析包含如下数据的多个文本文件:
[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 来获取节名“[Cluster1]”之后的第 n 行,但是该节名因文件而异...
$ip = Select-String -Path "$location" -Pattern "\[Cluster1\]" -Context 0,2 |
Foreach-Object {$_.Context.PostContext}
我试过在 SupportsCom=Yes 之前使用 Precontext 抓取第 N 行,但是“IpAddress=”的行位置因文件而异...
$ip = Select-String -Path "$location" -Pattern " SupportsCom=Yes" -Context 14,0 |
Foreach-Object { $_.Line,$_.Context.PreContext[0].Trim()}
有没有办法获取包含“SupportsCom=Yes”的部分,知道该部分由上下空行分隔,然后在该部分中搜索包含“IpAddress=”的字符串,然后 return “=”后面的值 ?
好的,因为不允许您使用模块(也许以后......),这应该可以满足您的需求
# 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
所在部分的名称,您可以使用此“one-liner”(为了便于阅读,分成多行) :
$ip = (Get-Content $location -Raw) -split '\[.+?\]' |
ConvertFrom-StringData |
Where-Object { $_.SupportsCom -eq 'Yes' -and $_.SupportsPos -eq 'Yes' } |
ForEach-Object IpAddress
Get-Content
行将输入文件作为单个 multi-line 字符串读取,并且 splits 它位于 headers 部分(例如[Cluster1]
)。ConvertFrom-StringData
将Key = Value
行转换为每节hashtable
行。- 对于每个哈希表,
Where-Object
检查它是否包含SupportsCom=Yes
和SupportsPos=Yes
ForEach-Object IpAddress
是 shorthand 用于编写Select-Object -ExpandProperty IpAddress
它为您提供IpAddress
的实际值而不是包含名为 [= 的成员的 object 13=].- 请注意,
$ip
可以是单个字符串值或字符串数组(如果有多个匹配部分)。
原回答:
您还可以编写一个 general-purpose 函数,将 INI 部分转换为 objects。这使您能够通过简单的 Where-Object
语句使用管道来获取您感兴趣的数据。
将 INI 部分输出为 objects 的通用函数,一个接一个:
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 工作原理的详细说明:
ForEach-Object IpAddress
是 shorthand 用于编写Select-Object -ExpandProperty IpAddress
它为您提供IpAddress
的实际值而不是包含名为 [= 的成员的 object 13=].- 请注意,
$ip
可以是单个字符串值或字符串数组(如果有多个匹配部分)。