在玩了一段時間的一些 powershell 腳本之后,我想知道是否有一個不使用 c# 的版本。感覺就像我錯過了一些關于如何正確管道的資訊。
$packages = Get-ChildItem "C:\Users\A\Downloads" -Filter "*.nupkg" |
%{ $_.Name }
# Select-String -Pattern "(?<packageId>[^\d] )\.(?<version>[\w\d\.-] )(?=.nupkg)" |
# %{ @($_.Matches[0].Groups["packageId"].Value, $_.Matches[0].Groups["version"].Value) }
foreach ($package in $packages){
$match = [System.Text.RegularExpressions.Regex]::Match($package, "(?<packageId>[^\d] )\.(?<version>[\w\d\.-] )(?=.nupkg)")
Write-Host "$($match.Groups["packageId"].Value) - $($match.Groups["version"].Value)"
}
最初我嘗試僅使用 powershell 執行此操作,并認為使用 @(1,2,3) 您可以創建一個陣列。
我最終通過使用 c# 而不是 powershell 執行正則運算式來繞過這個問題,這很有效,但我很好奇這將如何僅使用 powershell 完成。
雖然有 4 個包,但只做 powershell 版本就產生了 8 行。因此,像 $packages[0][0] 這樣訪問我的資料以獲取包 ID 從來沒有用過,因為 8 行是字串,而我希望回傳 4 個陣列
uj5u.com熱心網友回復:
不使用 c#的術語說明:您的意思是不直接使用.NET API。相比之下,C# 只是另一種可以使用此類 API 的基于 .NET 的語言,就像 PowerShell 本身一樣。
筆記:
下一部分回答了以下問題:如何避免直接呼叫 .NET API 來為我的正則運算式匹配代碼而使用 PowerShell 原生命令(運算子、自動變數)?
請參閱底部部分,了解
Select-String您真正目標的解決方案;tl;博士是:# Note the `, `, which ensures that the array is output *as a single object* %{ , @($_.Matches[0].Groups["packageId"].Value, $_.Matches[0].Groups["version"].Value) }
您的代碼的PowerShell 原生(接近)等效項是(請注意,假設$package包含輸入檔案的內容):
# Caveat: -match is case-INSENSITIVE; use -cmatch for case-sensitive matching.
if ($package -match '(?<packageId>[^\d] )\.(?<version>[\w\d\.-] )(?=.nupkg)') {
"$($Matches['packageId']) - $($Matches['Version'])"
}
-match,正則運算式匹配運算子,相當于[System.Text.RegularExpressions.Regex]::Match()(您可以將其縮短為[regex]::Match()),因為它只查找(最多)一個匹配項。注意區分大小寫:(
-match及其很少使用的別名-imatch)默認情況下不區分大小寫,就像所有 PowerShell 運算子一樣;對于區分大小寫的匹配,請使用c-prefixed 變體-cmatch.相比之下,.NET API默認區分大小寫;您必須將
[System.Text.RegularExpressions.RegexOptions]::IgnoreCase標志傳遞給以[regex]::Match()進行不區分大小寫的匹配(您可以使用'IgnoreCase'PowerShell 為您自動轉換的 )。從 PowerShell 7.2.x 開始,沒有與相關的 return-ALL-matches .NET API 等效的運算子,
[regex]::Matches(). 有關引入一個名為. _-matchall
但是,不是直接回傳描述匹配(或不匹配)的物件,而是
-match回傳一個Boolean,即$trueor$false,以指示匹配是否成功。僅當
-match回傳時$true,有關匹配的資訊才可用,即通過自動$Matches變數,這是反映輸入字串匹配部分的哈希表0:條目始終是完全匹配,可選的附加條目反映任何捕獲組 ((...)) 捕獲的內容,通過index,如果它們是匿名的(以 開頭1),或者在您的情況下,對于命名的捕獲組((?<name>...))按name。語法說明:鑒于 PowerShell 允許使用點表示法(屬性訪問語法),即使是哈希表,上述命令也可以使用
$Matches.packageId而不是$Matches['packageId'],例如,它也適用于數字(基于索引)條目,例如,$Matches.0而不是$Matches[0]警告:如果將陣列(可列舉)用作 LHS 運算元,則
-match' 行為會發生變化:$Matches未填充。- 進行過濾;也就是說,不是回傳一個指示匹配是否成功的布林值,而是回傳匹配輸入字串的子陣列。
請注意,哈希
$Matches表僅提供匹配的字串,而不提供諸如索引和長度之類的元資料[regex]::Match(),如在型別為 的回傳物件中找到的[System.Text.RegularExpressions.Match]。
Select-String解決方案:
$packages |
Select-String '(?<packageId>[^\d] )\.(?<version>[\w\d\.-] )(?=.nupkg)' |
ForEach-Object {
"$($_.Matches[0].Groups['packageId'].Value) - $($_.Matches[0].Groups['version'].Value)"
}
Select-String輸出Microsoft.PowerShell.Commands.MatchInfo實體,其.Matches集合包含一個或多個[System.Text.RegularExpressions.Match]實體,即與回傳的型別相同的實體[regex]::Match()- 除非
-AllMatches也通過,.Matches只有一個條目,因此使用[0]來定位上面的那個條目。
- 除非
如您所見,使用Select-Object的輸出物件最終要求您使用與[regex]::Match()直接呼叫時相同的 .NET 型別。
但是,不需要任何方法呼叫Get-Member,并且可以通過cmdlet在 PowerShell 中輕松發現輸出物件的屬性。
如果要在鋸齒狀陣列中捕獲匹配項:
$capturedStrings = @(
$packages |
Select-String '(?<packageId>[^\d] )\.(?<version>[\w\d\.-] )(?=.nupkg)' |
ForEach-Object {
# Output an array of all capture-group matches,
# *as a single object* (note the `, `)
, $_.Matches[0].Groups.Where({ $_.Name -ne '0' }).Value
}
)
這將回傳一個陣列陣列,其中的每個元素都是給定包的捕獲組匹配陣列,因此例如$capturedStrings[0][0]回傳packageId第一個包的值。
筆記:
$_.Matches[0].Groups.Where({ $_.Name -ne '0' }).Value以編程方式列舉所有捕獲組匹配項,并使用成員訪問列舉.Value將它們的屬性值作為陣列回傳;請注意必須如何排除 name ,因為它代表整個匹配項。'0'使用您的特定正則運算式中的捕獲組,以上內容等同于以下內容,如您問題中的注釋行所示:
@($_.Matches[0].Groups['packageId'].Value, $_.Matches[0].Groups['version'].Value)
, ...,陣列構造運算子的一元形式,用作將陣列(...此處用符號表示)作為一個整體輸出為單個物件的快捷方式。默認情況下,會進行列舉,并且元素將被一一發射。, ...實際上是概念上更清晰的捷徑Write-Output -NoEnumerate ...-有關該技術的解釋,請參見此答案。此外,
@(...)需要陣列子運算式運算子,以確保即使在所有$packages.
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/513269.html
