regex - PowerShell 将多行与正则表达式模式匹配
问题描述
我编写了一个 Powershell 脚本和正则表达式来搜索两个配置文本文件以查找管理 Vlan 的匹配项。例如,每个文本文件有两个管理 vlan,配置如下:
配置1.txt
123 MGMT_123_VLAN
234 MGMT_VLAN_234
配置2.txt
890 MGMT_VLAN_890
125 MGMT_VLAN_USERS
下面是我的脚本。它有几个问题。
首先,如果我使用 运行脚本,$Mgmt_vlan = Select-String -Path $File -Pattern $String -AllMatches
则屏幕输出显示预期的四 (4) 个 Mgmt vlan,但在 CSV 文件中输出显示如下
Filename Mgmt_vlan
Config1.txt System.Object[]
Config2.txt System.Object[]
我运行了脚本,控制台屏幕上的输出正好显示了我期望的四 (4) 个管理 vlan,但在 CSV 文件中却没有。它只显示这些 vlan
其次,如果我运行脚本$Mgmt_vlan = Select-String -Path $File -Pattern $String | Select -First 1
然后CSV显示如下:
Filename Mgmt_vlan
Config1.txt 123 MGMT_123_VLAN
Config2.txt 890 MGMT_VLAN_890
第二种方法Select -First 1
似乎只选择文件中的第一个匹配项。我尝试将其更改为Select -First 2
,然后 CSV 将 Mgmt_Vlan 列显示为System.Object[]
.
屏幕上的结果输出准确地显示了四 (4) 个 Mgmt Vlan,如预期的那样。
$folder = "c:\config_folder"
$files = Get-childitem $folder\*.txt
Function find_management_vlan($Text)
{
$Vlan = @()
foreach($file in files) {
Mgmt_Vlan = Select-String -Path $File -Pattern $Text -AllMatches
if($Mgmt_Vlan) # if there is a match
{
$Vlan += New-Object -PSObject -Property @{'Filename' = $File; 'Mgmt_vlan' = $Mgmt_vlan}
$Vlan | Select 'Filename', 'Mgmt_vlan' | export-csv C:\documents\Mgmt_vlan.csv
$Mgmt_Vlan # test to see if it shows correct matches on screen and yes it did
}
else
{
$Vlan += New-Object -PSObject -Property @{'Filename' = $File; 'Mgmt_vlan' = "Mgmt Vlan Not Found"}
$Vlan | Select 'Filename', 'Mgmt_vlan' | Export-CSV C:\Documents\Mgmt_vlan.csv
}
}
}
find_management_vlan "^\d{1,3}\s.MGMT_"
解决方案
正则表达式校正
首先,这段代码有很多错误。所以这可能不是您实际使用的代码。
其次,该模式不会匹配您的字符串,因为如果您使用"^\d{1,3}\s.MGMT_"
,您将匹配 1-3 个数字、任何空白字符(等于 [\r\n\t\f\v ])、任何字符(行终止符除外)和 MGMT_ 字符以及之后的任何内容。所以不是你想要的。因此,在您的情况下,您可以使用例如 this:^\d{1,3}\sMGMT_
或 with\s+
进行多个匹配。
代码更正
现在回到您的代码...您创建数组 $Vlan,没关系。
之后,您尝试获取所有字符串(在您的情况下,从目录中的每个文件中获取 2 个字符串)并使用两个复杂对象创建 PSObject。一个是 System.IO 中的 FileInfo,第二个是 System.IO 中的字符串数组 (String[])。
.ToString()
在正在处理的对象的每个属性上调用Export-Csv 函数。如果你在一个数组(即 Mgmt_vlan)上调用 .ToString() ,你会得到"System.Object[]"
,按照默认实现。因此,如果要从中制作 csv,则必须拥有一组“平面”对象。第二个大错误是创建一个具有多个职责的函数。在您的情况下,您的功能负责收集数据,然后负责导出数据。这是一个很大的不。因此,修复您的代码并将该导出移到其他地方。例如,您可以使用类似这样的东西(我使用了 get-content,因为我更喜欢它,但您可以使用任何您想要获取字符串集合的内容。
function Get-ManagementVlans($pattern, $files) { $Vlans = @() foreach ($file in $files) { $matches = (Get-Content $file.FullName -Encoding UTF8).Where({$_ -imatch $pattern}) if ($matches) { $Vlans += $matches | % { New-Object -TypeName PSObject -Property @{'Filename' = $File; 'Mgmt_vlan' = $_.Trim()} } } else { $Vlans += New-Object -TypeName PSObject -Property @{'Filename' = $File; 'Mgmt_vlan' = "Mgmt Vlan Not Found"} } } return $Vlans } function Export-ManagementVlans($path, $data) { #do something... $data | Select Filename,Mgmt_vlan | Export-Csv "$path\Mgmt_vlan.csv" -Encoding UTF8 -NoTypeInformation } $folder = "C:\temp\soHelp" $files = dir "$folder\*.txt" $Vlans = Get-ManagementVlans -pattern "^\d{1,3}\sMGMT_" -files $files $Vlans Export-ManagementVlans -path $folder -data $Vlans```
概括
但在我看来,在这种情况下是过度编程来创建像你一样的东西。您可以在 oneliner 中轻松完成(但如果文件不包含任何内容,则您没有信息)。powershell 的强大之处在于:
$pattern = "^\d{1,3}\s+MGMT_"
$path = "C:\temp\soHelp\"
dir $path -Filter *.txt -File | Get-Content -Encoding UTF8 | ? {$_ -imatch $pattern} | select @{l="FileName";e={$_.PSChildName}},@{l="Mgmt_vlan";e={$_}} | Export-Csv -Path "$path\Report.csv" -Encoding UTF8 -NoTypeInformation
或使用选择字符串:
dir $path -Filter *.txt -File | Select-String -Pattern $pattern -AllMatches | select FileName,@{l="Mgmt_vlan";e={$_.Line}} | Export-Csv -Path "$path\Report.csv" -Encoding UTF8 -NoTypeInformation
推荐阅读
- python - 如何在 youtube-dl 和 discord.py 中使用关键词而不是 url?
- javascript - 如何在 Dropzone Upload 上更改文件名?
- r - R中数据框每列的第25个分位数
- javascript - 笑话。测试在一个函数中执行 POST 和 fetch 的异步函数
- mysql - 在 WHERE 子句中使用来自 SELECT 的 COALESCE 的别名
- c++ - 现代 Cpp 项目中的 I18n
- java - 查询具有特定条件的 Spring DATA
- laravel - Laravel:更新作曲家错误并创建项目
- android - 使用 Gluon-mobile ShareService 从 android 上的应用程序打开 PDF 时出现异常
- twig - ServiceNotFoundException:依赖于不存在的服务“twig”