powershellsortingcode-reuse

Reusable sort-function in PowerShell


I have a list of strings containing the names of scripts. I need to check the versions of these scripts in order to figure out whether they have a version lower than the latest executed version and to only execute those scripts with a version number higher than the latest version, in order of the script sequence for that version. I also have a list of strings for the already executed scripts, so in order to determine the latest script, that list needs to be sorted.

Thus, I wrote some code to parse the version details from these strings and wanted to sort both lists with the same "function" - but I can't get the sorting to work. The Sort-Object part works when I call it directly, but not with the function Sort-Scripts. I am new to PowerShell, so I must be overlooking something fundamental. But I can't figure out what.

function Get-SemVer($version) {
    $version -match '^(?<major>\d+)(\.(?<minor>\d+))?(\.(?<patch>\d+))?(\-(?<pre>[0-9A-Za-z\-\.]+))?(\+(?<build>[0-9A-Za-z\-\.]+))?$' | Out-Null
    if ($matches) {
        [PSCustomObject]@{
            Major = [int]$matches['major']
            Minor = [int]$matches['minor']
            Patch = [int]$matches['patch']
            PreReleaseLabel = [string]$matches['pre']
            BuildLabel = [string]$matches['build']
        }
    }
}

function ParseVersionDetails {
    param([string]$InputString)

    if ($InputString -match '^(?<version>.*?)_(?<sequence>\d+)_?(?<scriptname>.*)') {
        [PSCustomObject]@{
            Version = Get-SemVer $matches['version']
            Sequence = [int]$matches['sequence']
            ScriptName = $matches['scriptname']
            FullName = $InputString
        }
    }
}

function Sort-Scripts {
    process {
        @($Input) | Sort-Object -Property @{Expression={$_.Version.Major}}, @{Expression={$_.Version.Minor}}, @{Expression={$_.Version.Patch}}, @{Expression={$_.Version.PreReleaseLabel}}, @{Expression={$_.Version.BuildLabel}}, @{Expression={$_.Sequence}}
    }
}

$list = @(
    "7.9.0-dev-IRIS-Dyn_02_SomeScript",
    "7.9.0-dev-IRIS-Dyn_01_SomeScript",
    "7.9.1-prod-IRIS-Dyn+13_01_OtherScript",
    "7.8.5_02_AnotherScript",
    "7.8.5_01_AnotherScript"
)

"#### Doesn't work"
$list | ForEach-Object { ParseVersionDetails -InputString $_ } | Sort-Scripts

"####  Works"
$list | ForEach-Object { ParseVersionDetails -InputString $_ } | Sort-Object -Property @{Expression={$_.Version.Major}}, @{Expression={$_.Version.Minor}}, @{Expression={$_.Version.Patch}}, @{Expression={$_.Version.PreReleaseLabel}}, @{Expression={$_.Version.BuildLabel}}, @{Expression={$_.Sequence}}

"Done"

Solution

  • The Sort-Object part works when I call it directly, but not with the function Sort-Scripts

    You must pass all pipeline input to a single Sort-Object call, so do not use a process block:

    function Sort-Scripts {
      # *No* process block; no need for @(...)
      $input | Sort-Object -Property { $_.Version.Major }, { $_.Version.Minor }, { $_.Version.Patch }, { $_.Version.PreReleaseLabel }, { $_.Version.BuildLabel }, Sequence
    }
    
    

    Note: