-
-
Save alkampfergit/19f89c1a93cc1e7b9ec9bf501f2b9134 to your computer and use it in GitHub Desktop.
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 |
Done!
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
Thanks for the usefl script. I made some changes:
- Added
--include-unknown
to thewinget upgrade
command to avoid the issue mentioned below - Becasue some of the software installed on my system has non-ascii name, I worked around the quirks
- 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;
}
@houtianze I tried to run your function, but it generates an error.
Line |
1 | while (-not $lines[$fl].StartsWith("Name"))
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| You cannot call a method on a null-valued expression.
That's why I created a function that creates a array with an object for each value.
Of course, it can be improved, but we'll leave that for later...
function Get-Winget-Upgradables {
$apps = @()
$start = $false
# Remove unnecessary first lines
winget upgrade --accept-source-agreements --include-unknown | foreach-object {
if ( $psitem -match '^([-]+)$' ) {
$start = $true
}
elseif ( $start -eq $true ) {
$apps += $psitem
}
}
# Remove the last line
$apps = $apps[ 0..( $apps.length - 2 ) ]
# Loop the array and create an object for any value
$index = 0
$apps.foreach({
$pattern = "^(.+\u2026?)\s+([\u2026\.\w\+]+)\s+([\.\d]+)\s+([\.\d]+)\s+([\w]+)$"
$apps[ $index ] = @{
'name' = ( $apps[ $index ] -replace $pattern, '$1' ) -replace '\s+$', ''
'id' = $apps[ $index ] -replace $pattern, '$2'
'version' = $apps[ $index ] -replace $pattern, '$3'
'available' = $apps[ $index ] -replace $pattern, '$4'
'source' = $apps[ $index ] -replace $pattern, '$5'
}
$index += 1
})
return $apps
}
@F4Jonatas Thanks for the feedback. I tried again and it works on my computer, my guess is that your system is set to some non-English language that shows a localized string of Name
and that's why the function failed to locate the line and the index ran out of bound. Locating using ---...
is a better option in this case.
In addition to the language setting, it also makes a difference whether you use Powershell, Powershell ISE or Visual Studio Code.
This has a particular effect on the truncation of text. Sometimes it is a character string like  and sometimes it is a special character that looks like 3 dots (Unicode ellipsis [char]0x2026).
I encountered this problem in a similar project.
Might I suggest adding
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
near the top to account for issues that may occur when running this in PS5.1? An example of before and after running that command, if using the default output encoding of ascii:

