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

时间:2019-03-07 16:06:46

标签: powershell sorting version-numbering

所以,我有一个看起来像这样的版本列表:

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 | %{新对象System.Version($ _)} |分类   + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~       + CategoryInfo:InvalidOperation:(:) [New-Object],MethodInvocationException       + FullyQualifiedErrorId: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

4 个答案:

答案 0 :(得分:4)

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.0v2.9.0之后如何正确排序,而 lexical 排序则不会这样):

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.前缀。

答案 1 :(得分:2)

对字符串中包含的任何长度的数字进行排序的更通用的方法是Roman Kuzmin's $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

答案 2 :(得分: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


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

答案 3 :(得分:0)

问题是版本字符串中不允许使用'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
...