我发现自己最近处理了大量 DNS 数据,并且我一直在使用自定义排序表达式按名称的每个虚线对 FQDN 进行反向排序,以便同一区域/子区域中的记录彼此相邻排序。例如:
# Build some fake data that normally comes from API call results
# There are generally more columns in the results and they're not always the same
$fqdns = @'
"fqdn","otherdata"
"a.example.com","foo"
"a.example.org","foo"
"example.com","foo"
"example.org","foo"
"www.example.com","foo"
"www.example.org","foo"
"www.sub.example.com","foo"
"www.sub.example.org","foo"
'@ | ConvertFrom-Csv
$fqdns | sort @{E={$a=$_.fqdn.Split('.'); [array]::Reverse($a); $a -join '.'}}
排序表达式效果很好,但是在 shell 中交互工作时需要输入很多内容。我试图弄清楚如何将它作为变量或函数添加到我的配置文件中,以便我可以在输入更少的情况下重复使用它。最重要的是,我正在对其进行初始拆分的字符串属性并不总是被称为 fqdn
。 .所以我需要一些我仍然可以指定该值的东西。我设想能够输入这样的内容:$fqdns | sort (fqdnsort fqdn)
附言我不一定要在表达式本身中寻找效率改进,但如果您有想法,也欢迎。
最佳答案
您影响的自定义排名行为完全包含在您传递给 sort
的参数中。/Sort-Object
- 所以你所要做的就是编写一个返回这样一个对象的函数:
function Get-FqdnSortKey
{
return @{E={$a=$_.fqdn.Split('.'); [array]::Reverse($a); $a -join '.'}}
}
然后像这样使用:$fqdns |Sort-Object (Get-FqdnSortKey)
在进行任何其他功能修改之前,为了清楚起见,让我们稍微重构和重新格式化现有代码:function Get-FqdnSortKey
{
param()
$SortKeyExpression = {
$a=$_.fqdn.Split('.')
[array]::Reverse($a)
$a -join '.'
}
return @{Expression=$SortKeyExpression}
}
如果要在属性表达式中参数化源属性的名称,则必须使用 GetNewClosure()
关闭函数参数值。 :function Get-FqdnSortKey
{
param(
[string]$PropertyName = 'Fqdn'
)
$SortKeyExpression = {
$a = $_.$PropertyName.Split('.')
[array]::Reverse($a)
$a -join '.'
}.GetNewClosure()
return @{Expression=$SortKeyExpression}
}
这样,当 $PropertyName
在稍后的时间点由结果闭包解决,它仍然具有它在 Get-FqdnSortKey
中的值当我们定义它时。如果您想为数据添加内在排序行为,您可以通过使用 PowerShell 的
class
定义自定义数据类型来实现。关键字 - 内在排序的唯一要求是我们的类型实现了 System.IComparable
界面:class FqdnWithMetaData : IComparable
{
[string]$Fqdn
[string]$OtherData
hidden [string] $_namespaceOrder = $null
[int]
CompareTo([object]$other)
{
if($other -isnot [FqdnWithMetaData]){
throw [ArgumentException]::new('other')
}
return [string]::Compare($this.GetNamespaceOrder(), $other.GetNamespaceOrder(), [StringComparison]::InvariantCultureIgnoreCase)
}
hidden [string] GetNamespaceOrder()
{
if(-not $this._namespaceOrder){
$labels = $this.Fqdn.Split('.')
[array]::Reverse($labels)
$this._namespaceOrder = $labels -join '.'
}
return $this._namespaceOrder
}
}
现在我们已经实现了 IComparable
, Sort-Object
只需调用$current.CompareTo($next)
在输入上作为其比较例程的一部分:$data = @(
[FqdnWithMetaData]@{ Fqdn = 'yahoo.com' },
[FqdnWithMetaData]@{ Fqdn = 'google.com' },
[FqdnWithMetaData]@{ Fqdn = 'google.net' },
[FqdnWithMetaData]@{ Fqdn = 'amazon.com' }
)
$data |Sort-Object # No need to supply anything else here
# Resulting in
Fqdn OtherData
---- ---------
amazon.com
google.com
yahoo.com
google.net
见 about_Classes
help topic有关在 PowerShell 中使用自定义数据类型的更多信息
关于powershell - 可重用的自定义排序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63637157/