Сортировать список, который содержит номера версий правильно - PullRequest
2 голосов
/ 07 марта 2019

Итак, у меня есть список версий, который выглядит следующим образом:

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: исключение вызывает ".ctor" с аргументом "1": "Часть строки версии была слишком короткой или слишком длинной. "В строке: 1 символ: 24 + $ версии = $ теги |% {new-object System.Version ($ _)} |sort + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + 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 = Get-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 ]

4 голосов
/ 07 марта 2019

ТЛ; др

Позже вы указали, что ваш массив $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 затем получает только теги версий в их первоначальном виде (в виде строк с префиксом * 1038), правильно отсортированными.


Префикс 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. в именах типов.

2 голосов
/ 07 марта 2019

Более общий способ сортировки чисел любой длины, содержащихся в строках: Роман Кузьмин $ 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
0 голосов
/ 07 марта 2019

Проблема в том, что 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
...
0 голосов
/ 07 марта 2019

Я считаю, что вам нужно правильно анализировать эти строки как объекты версий, если вы хотите отсортировать их без проблем.


$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.

...