Powershell Get-Content 文本中的特定内容

Powershell Get-Content specific content inside text

我收到一个包含多个列表的文本文件,如下所示(编辑:包括更准确的示例数据集)

# 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

每个“SYSTEM 注释”都意味着它后面的一组新注释。 我想分别阅读每个内容块,因此应将每组内容分配给一个丢弃嵌入评论的对象。我只需要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]

但我不确定如何实际分别分配这些集合。 请帮忙

这是一种可能的解决方案。结果将是一个哈希表,每个键包含该集合的任何 ips 数组:

$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]}    

这是另一种方法。我们将利用 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}}

您可以使用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 将文件作为单个字符串读入,使 multi-line 匹配更容易。在 PowerShell 7 中,Select-String 包含一个 -Raw 开关,使此过程稍微简单一些。

这会输出匹配 (.*?) 的捕获组 1 结果。如果你想在评论之间而不是在 Set <something>Set <something> 之间进行捕获,你可以将最后的 -Pattern 值编辑为 # 而不是 # Set .

正则表达式分解:

  • # 按字面意思匹配字符 #
  • $section 替换您的变量值与字面上的值匹配,前提是字符串中没有正则表达式字符
  • .* 匹配任何字符(行终止符除外)
  • \r 匹配一个回车 return
  • ? 量词——匹配0到1次,次数为 可能,根据需要回馈(贪婪)
  • \n 匹配 line-feed(换行)字符
  • (?s)修饰符:单行。点匹配换行符
  • 第 1 个捕获组 (.*?)
  • .*? 延迟匹配任何字符
  • 正前瞻(?=\r?\n# Set)
  • \r? 匹配回车 return 零次或多次
  • \n 匹配 line-feed(换行)字符
  • # 设置匹配字符 # Set 字面意思
  • $匹配字符串结尾

如果我正确理解了新示例的问题,您想解析文件并创建单个变量,每个变量都包含一个数组 ip 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 {}
}

现在 $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(其他同理)