Skip to content

Instantly share code, notes, and snippets.

@alkampfergit
Last active January 14, 2024 19:38
Show Gist options
  • Save alkampfergit/19f89c1a93cc1e7b9ec9bf501f2b9134 to your computer and use it in GitHub Desktop.
Save alkampfergit/19f89c1a93cc1e7b9ec9bf501f2b9134 to your computer and use it in GitHub Desktop.
Winget upgrade output parsed into a real Powershell Object
class Software {
[string]$Name
[string]$Id
[string]$Version
[string]$AvailableVersion
}
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
$upgradeResult = winget upgrade | Out-String
$lines = $upgradeResult.Split([Environment]::NewLine)
# Find the line that starts with Name, it contains the header
$fl = 0
while (-not $lines[$fl].StartsWith("Name"))
{
$fl++
}
# Line $i has the header, we can find char where we find ID and Version
$idStart = $lines[$fl].IndexOf("Id")
$versionStart = $lines[$fl].IndexOf("Version")
$availableStart = $lines[$fl].IndexOf("Available")
$sourceStart = $lines[$fl].IndexOf("Source")
# Now cycle in real package and split accordingly
$upgradeList = @()
For ($i = $fl + 1; $i -le $lines.Length; $i++)
{
$line = $lines[$i]
if ($line.Length -gt ($availableStart + 1) -and -not $line.StartsWith('-'))
{
$name = $line.Substring(0, $idStart).TrimEnd()
$id = $line.Substring($idStart, $versionStart - $idStart).TrimEnd()
$version = $line.Substring($versionStart, $availableStart - $versionStart).TrimEnd()
$available = $line.Substring($availableStart, $sourceStart - $availableStart).TrimEnd()
$software = [Software]::new()
$software.Name = $name;
$software.Id = $id;
$software.Version = $version
$software.AvailableVersion = $available;
$upgradeList += $software
}
}
$upgradeList | Get-Member
$upgradeList | Format-Table
@alkampfergit
Copy link
Author

Done!

@Warrentheo
Copy link

Warrentheo commented Sep 13, 2023

My output includes the following line at the end:
2 package(s) have version numbers that cannot be determined. Use --include-unknown to see all results.
and it doesn't get handled correctly,

For ($i = $fl + 1; $i -le $lines.Length; $i++)
{
$line = $lines[$i]
if ($line.Length -gt ($availableStart + 1) -and -not $line.StartsWith('-') -and -not $line.Contains("package(s) have version numbers"))
{

Here is the suggested edit that just ignores these lines

@houtianze
Copy link

houtianze commented Jan 14, 2024

Thanks for the usefl script. I made some changes:

  1. Added --include-unknown to the winget upgrade command to avoid the issue mentioned below
  2. Becasue some of the software installed on my system has non-ascii name, I worked around the quirks
  3. Made this code snippet to a function. (This is just for my use case)
function Get-Winget-Upgradables
{

  [Console]::OutputEncoding = [System.Text.Encoding]::UTF8
  $upgradeResult = winget upgrade --include-unknown | Out-String

  $lines = $upgradeResult.Split([Environment]::NewLine)


  # Find the line that starts with Name, it contains the header
  $fl = 0
  while (-not $lines[$fl].StartsWith("Name"))
  {
    $fl++
  }

  # Line $i has the header, we can find char where we find ID and Version
  $idStart = $lines[$fl].IndexOf("Id")
  $versionStart = $lines[$fl].IndexOf("Version")
  $availableStart = $lines[$fl].IndexOf("Available")
  $sourceStart = $lines[$fl].IndexOf("Source")

  # Now cycle in real package and split accordingly
  $upgradeList = @()
  For ($i = $fl + 1; $i -le $lines.Length; $i++) 
  {
    $line = $lines[$i]
    if ($line.Length -gt ($sourceStart + 1) -and -not $line.StartsWith('-'))
    {
      # when the name of the software is non-ascii, rune counting becomes messy.
      $nameWithSpaceEnding = $line.Substring(0, $idStart)
      $nameWithSpaceEndingByteCount = [System.Text.Encoding]::UTF8.GetByteCount($nameWithSpaceEnding)
      $nameOffset = ($nameWithSpaceEndingByteCount - $nameWithSpaceEnding.Length) / 2;
      $name = $line.Substring(0, $idStart - $nameOffset).Trim()
      # need to Trim() instead of TrimEnd() because somehow when run with | Out-String,
      # the winget output of name becomse ellipsized... Trim() would work around this.
      $id = $line.Substring($idStart - $nameOffset, $versionStart - $idStart).Trim()
      $version = $line.Substring($versionStart - $nameOffset, $availableStart - $versionStart).Trim()
      $available = $line.Substring($availableStart - $nameOffset, $sourceStart - $availableStart).Trim()
      $software = [WingetSoftware]::new()
      $software.Name = $name;
      $software.Id = $id;
      $software.Version = $version
      $software.AvailableVersion = $available;

      $upgradeList += $software
    }
  }

  return $upgradeList;
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment