0

I am trying to write a script, which will return a formatted version number.

However: due to winget outputting text, I have to first convert it to object, using the

Then I can get rid of the ID, which I have no use for and display only name and version.

Here's the code, taken from this answer:

function ConvertFrom-FixedColumnTable {
  [CmdletBinding()]
  param(
    [Parameter(ValueFromPipeline)] [string] $InputObject
  )
  
  begin {
    Set-StrictMode -Version 1
    $lineNdx = 0
  }
  
  process {
    $lines = 
      if ($InputObject.Contains("`n")) { $InputObject.TrimEnd("`r", "`n") -split '\r?\n' }
      else { $InputObject }
    foreach ($line in $lines) {
      ++$lineNdx
      if ($lineNdx -eq 1) { 
        # header line
        $headerLine = $line 
      }
      elseif ($lineNdx -eq 2) { 
        # separator line
        # Get the indices where the fields start.
        $fieldStartIndices = [regex]::Matches($headerLine, '\b\S').Index
        # Calculate the field lengths.
        $fieldLengths = foreach ($i in 1..($fieldStartIndices.Count-1)) { 
          $fieldStartIndices[$i] - $fieldStartIndices[$i - 1] - 1
        }
        # Get the column names
        $colNames = foreach ($i in 0..($fieldStartIndices.Count-1)) {
          if ($i -eq $fieldStartIndices.Count-1) {
            $headerLine.Substring($fieldStartIndices[$i]).Trim()
          } else {
            $headerLine.Substring($fieldStartIndices[$i], $fieldLengths[$i]).Trim()
          }
        } 
      }
      else {
        # data line
        $oht = [ordered] @{} # ordered helper hashtable for object constructions.
        $i = 0
        foreach ($colName in $colNames) {
          $oht[$colName] = 
            if ($fieldStartIndices[$i] -lt $line.Length) {
              if ($fieldLengths[$i] -and $fieldStartIndices[$i] + $fieldLengths[$i] -le $line.Length) {
                $line.Substring($fieldStartIndices[$i], $fieldLengths[$i]).Trim()
              }
              else {
                $line.Substring($fieldStartIndices[$i]).Trim()
              }
            }
          ++$i
        }
        # Convert the helper hashable to an object and output it.
        [pscustomobject] $oht
      }
    }
  }
  
}

  
[Console]::OutputEncoding = [System.Text.UTF8Encoding]::new() 

(winget list --name "ourSoft") -match '^(\p{L}|-)' | # filter out progress-display lines
ConvertFrom-FixedColumnTable |    # parse output into objects
Select-Object -Property Name, Version | # show only Name and Version
Sort-Object Name |                  # sort by the Name property (column)
Format-Table                      # display the objects in tabular format

I am trying to do the last step, which will return a version number different in what is stored in OS:

  • 5.4.33676.0 - stored number in OS

  • 5.4.0.33676 - what I want to return (part 3 and 4, divided by a dot, are swapped)

Is there any way to do that?

I am trying to write my own function for that, but it seems CharArray is not filling up + I don´t know how to make the piped output display the modified numbers instead of the former ones:

function ModifyVersionNumber {  
    $VersionNumber = Select-Object -Property Version
    $CharArray =$VersionNumber.Split(".")
    $ModifiedCharArray = $CharArray[0] + "." + $CharArray[1] + "." + $CharArray[3] + "." + $CharArray[2]
    $ModifiedCharArray  
}
  
[Console]::OutputEncoding = [System.Text.UTF8Encoding]::new() 

(winget list --name "ourSoft") -match '^(\p{L}|-)' | # filter out progress-display lines
ConvertFrom-FixedColumnTable |    # parse output into objects 
ModifyVersionNumber | 
Select-Object -Property Name, Version | # show only Name and Version
Sort-Object Name |                  # sort by the Name property (column)
Format-Table                      # display the objects in tabular format
mklement0
  • 382,024
  • 64
  • 607
  • 775
Blu3Rat
  • 49
  • 7

2 Answers2

2

If I try to run your function as provided against a test object, I get a clue as to why it's not working:

$o = [pscustomobject]@{version = '5.4.0.33676'};
$o | ModifyVersionNumber;

gives me:

You cannot call a method on a null-valued expression.
At line:3 char:5
+     $CharArray =$VersionNumber.Split(".")
+     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull

Cannot index into a null array.
At line:4 char:5
+     $ModifiedCharArray = $CharArray[0] + "." + $CharArray[1] + "." +  ...
+     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : NullArray

That initial select-object doesn't have anything to operate on. I rewrote the function to take a parameter from the pipeline and it works for my test case:

function ModifyVersionNumber {
    param ([parameter(ValueFromPipeline = $true)]$Versions)
    process{
        foreach ($obj in $Versions) {
            $VersionNumber = $obj.Version;
            $CharArray =$VersionNumber.Split(".")
            $ModifiedCharArray = $CharArray[0,1,3,2] -join '.'
            $obj.Version = $ModifiedCharArray;
            $obj;
        }
    }
}

Also, for your consideration: $ary = @(1,2,3,4); $ary[0,1,3,2] -join '.' would help to significantly reduce the code where you're re-assembling the version string.

Ben Thul
  • 31,080
  • 4
  • 45
  • 68
  • Thanks for both of your comments, Ben-Thul and @mathias -r-jessen. Ben, your function is working, but it always outputs out only one result - I guess the last one. ''' (winget list --name "ourSoft") ''' Actually produces several results and I would need all of them to have that version umber changed> ''' ourSoft 1 5.4.33676.0 ourSoft 2 5.1.33205.1 ourSoft 3 5.3.33502.0 ''' I guess I need to do a for loop over the whole helper hashtable. I tried to put it inside the ConvertFrom-FixedColumnTable function, but that produces the same errer about null-valued expression. – Blu3Rat Aug 11 '23 at 13:17
  • 1
    No need to loop externally. I've modified the function to accommodate an array of items being passed to it. – Ben Thul Aug 11 '23 at 14:36
  • Thanks a lot, that´s what I needed. – Blu3Rat Aug 15 '23 at 08:47
0

There's a number of ways to go about this. You could split the version string into its individual components and then manually join them together in the right order:

$version = '5.4.33676.0'
$major,$minor,$build,$revision = $version.Split('.')
$version = $major,$minor,$revision,$build -join '.'

Or you could use the -replace regex operator to replace the two last components with eachother:

$version = '5.4.33676.0'
$version = $version -split '\.(\d+)\.(\d+)$', '.$2.$1'
Mathias R. Jessen
  • 157,619
  • 12
  • 148
  • 206