对包含版本号的列表进行正确排序



所以,我有一个版本列表,如下所示:

v1.1.0
v1.2.0
v1.3.0
v1.4.0
v1.5.0
v1.7.0
v1.8.0
v1.9.0
v2.0.0
v2.1.0
v2.10.0
v2.11.0
v2.12.0
v2.2.0
v2.3.0
v2.4.0
v2.5.0
v2.6.0
v2.7.0
v2.8.0
v2.9.0

问题是,它们的顺序不正确。 我是Powershell的新手,所以我在尝试对它们进行排序时遇到了一些问题。 我试图这样做:

$tags = git tag
$versions = $tags | %{ new-object System.Version ($_) } | sort

但是我收到此错误:

new-object:使用"1"参数调用".ctor"的异常:"版本字符串部分太短或太长。 行:1 字符:24 + $versions = $tags |%{ new-object System.Version ($_) } |排序 + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + 类别信息 : 无效操作: (:) [新对象], 方法调用异常 + FullQualifiedErrorId : ConstructorInvokedThrowException,Microsoft.PowerShell.Commands.NewObjectCommand

谁能帮忙?


更新

我使用了如下所示的解决方案之一:

$location = Get-Location
$path = $location.tostring() + "CHANGELOG.md"
$tags = git tag
$i = 0
Clear-Content $path
Add-Content $path "Change Log"
Add-Content $path "=========="
Add-Content $path " "
$ToNatural = { [regex]::Replace($_, 'd+', { $args[0].Value.PadLeft(20) }) }
$tags | Sort-Object $ToNatural
foreach($tag in $tags) 
{
if (-NOT ($tag -match "v(d+.)(d+.)(*|d+)")) { continue }
$i = $i + 1
if ($i -eq 0) { continue }
$tag
If ($i -gt 0) {
$previous = $tags[$i - 1]
Add-Content $path " "
}
}

这种工作,但所有标签似乎都被控制台记录,它显示如下:

1.6.0
changeDeliveryFieldAccess
orders/autoComplete
returns/autoComplete
save-lines-dates
services/serviceDetails
tile-colours
users/confirmation
v0.1
v1.1.0
v1.2.0
v1.3.0
v1.4.0
v1.5.0
v1.7.0
v1.8.0
v1.9.0
v2.0.0
v2.1.0
v2.2.0
v2.3.0
v2.4.0
v2.5.0
v2.6.0
v2.7.0
v2.8.0
v2.9.0
v2.10.0
v2.11.0
v2.12.0
v.2.7.1

正如你所看到的,里面有一些我不想要的。具体说来:

1.6.0
changeDeliveryFieldAccess
orders/autoComplete
returns/autoComplete
save-lines-dates
services/serviceDetails
tile-colours
users/confirmation
v.2.7.1

一旦这些从我的列表中清除,那么顺序将是正确的:)


更新 2

所以我尝试了另一种希望更好的解决方案:

$location = 获取位置 $path = $location.tostring() + "\CHANGELOG.md" $tags = git 标签 $i = 0

Clear-Content $path
Add-Content $path "#Change Log"
Add-Content $path "=========="
Add-Content $path " "
$tags |
Where-Object { $_.Substring(1) -as [version] } |
Sort-Object { [version] $_.Substring(1) }
foreach($tag in $tags) {
write-host "$($tag) is ok"
}

我不确定我是否做对了,但这是上面代码的输出:

1.6.0 is ok
changeDeliveryFieldAccess is ok
orders/autoComplete is ok
returns/autoComplete is ok
save-lines-dates is ok
services/serviceDetails is ok
tile-colours is ok
users/confirmation is ok
v.2.7.1 is ok
v0.1 is ok
v1.1.0 is ok
v1.2.0 is ok
v1.3.0 is ok
v1.4.0 is ok
v1.5.0 is ok
v1.7.0 is ok
v1.8.0 is ok
v1.9.0 is ok
v2.0.0 is ok
v2.1.0 is ok
v2.10.0 is ok
v2.11.0 is ok
v2.12.0 is ok
v2.2.0 is ok
v2.3.0 is ok
v2.4.0 is ok
v2.5.0 is ok
v2.6.0 is ok
v2.7.0 is ok
v2.8.0 is ok
v2.9.0 is ok

tl;博士

您稍后指出您的$tags数组还包含其他非版本字符串,因此必须过滤掉这些字符串:

$sortedVersionTags = $tags |
Where-Object { $_.Substring(1) -as [version] } |
Sort-Object { [version] $_.Substring(1) }
  • Where-Object { $_.Substring(1) -as [version] }只传递那些可以转换为[version](System.Version)对象的字符串 --as [version]- 在开头删除v后(.Substring(1);忽略删除v是你最初的问题);-as运算符要么返回成功转换的值,要么返回$null

  • 然后,Sort-Object将筛选的标记作为版本号进行排序,从而生成正确的顺序 - 有关说明,请参阅下一节。

  • 然后,$sortedVersionTags仅接收版本标记,其原始形式(作为前缀v字符串)正确排序。


版本号中的v前缀会阻止它们转换为[System.Version]实例;只需先将其删除(不是从输入本身中删除;只是暂时删除,以便创建版本信息对象,例如v1.1.0->1.1.0)。

此外,还可以简化您的命令:

# $tags is an array of lines, as output by `git tag`
$tags | Sort-Object { [version] $_.Substring(1) }
  • [version]是内置在PowerShell中的类型加速器(简称),指的是[System.Version][1]

  • 你可以简单地将字符串转换为[version],这比使用New-Object更简洁、更快捷。

  • Sort-Object通过脚本块 ({ ... }) 接受表达式,而不是要排序的固定属性; 在脚本块中,$_引用给定的输入对象;$_.Substring(1)只是删除第一个字符(v)。

    • 请注意,表达式仅用于排序目的;Sort-Object仍然输出原始字符串- 已排序。

使用您的示例输入,上述结果(请注意v2.10.0如何在v2.9.0后正确排序,而词法排序则不是这种情况):

v1.1.0
v1.2.0
v1.3.0
v1.4.0
v1.5.0
v1.7.0
v1.8.0
v1.9.0
v2.0.0
v2.1.0
v2.2.0
v2.3.0
v2.4.0
v2.5.0
v2.6.0
v2.7.0
v2.8.0
v2.9.0
v2.10.0
v2.11.0
v2.12.0

如果您希望输出System.Version实例而不是输入字符串,则该命令将变得更加简单(PSv3+ 语法):

[version[]] $tags.Substring(1) | Sort-Object

如果可能无法以这种方式转换$tags中包含的所有字符串(由于没有v<version>格式),请使用以下内容(PSv4+ 语法):

# Reports non-convertible lines as non-terminating errors, but processes all others.
$tags.ForEach({ [version] $_.Substring(1) }) | Sort-Object

此方法可确保遇到无法转换的字符串不会破坏整个命令

  • 那些可以转换的是,并且是输出。

  • 那些无法转换的将导致错误打印到控制台,并且之后也会反映在自动$Error集合中。您可以使用2>$null抑制控制台输出。


[1] 通常,PowerShell 允许您省略类型名称中的System.前缀。

对字符串中包含的任何长度的数字进行排序的更通用方法是 Roman Kuzmin $ToNatural

如果将其存储在脚本/配置文件中:

$ToNatural = { [regex]::Replace($_, 'd+', { $args[0].Value.PadLeft(20) }) }

您可以简单地使用:

$tags | Sort-Object $ToNatural
v1.1.0
v1.2.0
v1.3.0
...
v2.7.0
v2.8.0
v2.9.0
v2.10.0
v2.11.0
v2.12.0

如果您想毫无问题地对它们进行排序,您需要将这些字符串正确解析为版本对象。


$tags = @(
'v1.1.0'
'v1.2.0'
'v1.3.0'
'v1.4.0'
'v1.5.0'
'v1.7.0'
'v1.8.0'
'v1.9.0'
'v2.0.0'
'v2.1.0'
'v2.10.0'
'v2.11.0'
'v2.12.0'
'v2.2.0'
'v2.3.0'
'v2.4.0'
'v2.5.0'
'v2.6.0'
'v2.7.0'
'v2.8.0'
'v2.9.0'
)

#----------------------------------------------------------------------------# 
#     Parse Will Fail As 'v' In String is Not Valid Semantic Versioning      # 
#----------------------------------------------------------------------------# 
$tags | % { 
$tag = $_
$version = [version]::new()
if ([version]::TryParse($tag, [ref]$version))
{
$version
}
else 
{"ParseFailed--$($tag)"}
} | Sort-Object

#----------------------------------------------------------------------------# 
#                        Parsing String Successfully                         # 
#----------------------------------------------------------------------------# 
$tags | % { 
$tag = $_ -replace '[a-zA-Z]'
$version = [version]::new()
if ([version]::TryParse($tag, [ref]$version))
{
$version
}
else 
{"ParseFailed--$($tag)"}
} | Sort-Object

如果还想将其用作2.1.0类型的字符串,则可以对返回的对象进行ToString()

问题是版本字符串中不允许使用"v"。 如果先将其删除,则可以正确排序:

$tags | Sort-Object {[Version]($_ -replace "[a-zA-Z]","")}

请注意,这并没有真正删除"v"(它会创建一个没有它的副本),它仍然会在输出中存在:

v1.1.0
v1.2.0
v1.3.0
v1.4.0
v1.5.0
...

相关内容

  • 没有找到相关文章