Script adding +1

Sorry, not a great description but tricky to sum up !
I have the below script, so when i select the “option” it will uninstall the app via the string. (As you can see its a working progress).

If i choose 7 for example it adds 1 to it and gives me the option for 8 !
The only way to get round this is do $a = [int]($a - ‘1’)

Is this my best way around or am i missing something ?

function Uninstall-Applications
     param (
     [string]$Title = 'Uninstall Application List',
     [regex]$Criteria = '^[0-99]$'


     Write-host -ForegroundColor Yellow " Please select index number for application to Uninstall : "
     Write-Host -ForegroundColor Yellow " Q: Press 'Q' to quit."
     $table | ft -AutoSize

try {
    $32Uninstall = Get-ItemProperty HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\* 
Catch {
    write-error $_.exception.message

Try {
$Table = $32Uninstall.displayname | 
ForEach-Object { $option = 1 } {
    [PSCustomObject] @{ Option = $option; '-' = ":" ;Displayname = $_ } ;
    Catch {
    write-error $_.exception.message

Write-Host "================ $Title ================"

     $a = Read-Host "Please make a selection"
  if ([Regex]::IsMatch($a,$Criteria)) {
  $a = [int]($a -'1')
  $Table[$a] }
  else {
  write-output "invalid selection" }
#until ($input -eq 'q')

First things first …
What are you attempting with …

[regex]$Criteria = '^[0-99]$'

And why do you have the Title in the param block?

ok, so the results that come back from the uninstall (generally most people don’t have 99 apps ! ) and a number between 1 and 99 must be selected.

The Regex is wrong for what you want to do. And at this point I think the param block needs to go away.

Do you always plan on running this in interactive mode where your asking a user to make a selection?

It’s a tool for desktop engineers and not users. To develop my piwershell skills I’ve decided to make some tools to aid our desktop guys.

So some further playing and this works but still adds the +1 to the selection. Must be to do with the $option++…

$Uninstall =  Get-ItemProperty HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\* |
     ForEach-Object { $option = 1 } {
 [PSCustomObject] @{ Option = $option; '-' = ":" ;Displayname = $_.displayname;Uninstall = $_.UninstallString } ;

$Uninstall | select Option, Displayname | ft -AutoSize
   $a = Read-Host "Please make a selection" 

   $uninstall[$a] | select Displayname, @{name="Uninstallstring";expression={$_.Uninstall}} -OutVariable Selection
   "string is: "


Any thoughts ?

I don’t know if it’s the code or my ISE environment, but when I run it, there is no title and $Table is blank. Since $Title and $Table exist only within the bounds of the function, they don’t exist in the main script.

I also don’t see the list displayed when running the script.

I’d also change some of the logic when building $Table:
$script:Table = $32Uninstall.displayname |
ForEach-Object { $option = 0 } {
[PSCustomObject] @{ Option = $option; ‘-’ = “:” ;Displayname = $_ } ;

This avoids $option’s value from being greater than the actual number of options.

Have you run
$Uninstall = Get-ItemProperty HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall ?

(sorry for the daft question)

Is this a local scope $script:Table ?

The original script at the top of the posting simply had “$table” being populated.

When I ran the script, $table has data while in the Uninstall-Applications function but not in the mainline code.

I recommended the change to “$script:Table” to allow the variable to exist outside of the function.

Ahh, if it was a snake, it would have bit me.

$table is zero based. Option 1 is stored in $table[0] and option 7 is stored in $table[6]. So $table[7] contains option 8

Yes, you will have to subtract one.

Thanks Mark, appreciate your help.

The overall script conception unclear and against powershell rules
Can I suggest my variant ?

function Get-Applications {
    Get-ItemProperty HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\* 
	# here you can filter out some applications

function Show-ApplicationsForUninstall($applist) {
	$number = 1;
	Write-Host 'Uninstall Application List:'
	foreach($app in $applist) {
		Write-Host ('{0} - {1}' -f $number, $app.DisplayName)

function Get-AppNumberFromUser($maxcount) {
	Write-host -ForegroundColor Yellow " Please select index number for application to Uninstall [1..$maxcount]: "
	Write-Host -ForegroundColor Yellow " Q: Press 'Q' to quit."
	do {
		$userinput = Read-Host "Please make a selection"
	} until ($userinput -eq 'Q' -or ($userinput -match '^\d+$' -and [int]$userinput -ge 1 -and [int]$userinput -le $maxcount));
	# first match 'Q', next match integer input, last match >=1 and <=maxcount

function Uninstall-Application($app) {
	Write-Host ('Uninstalling app {0}' -f $app.DisplayName)
	#uninstall logic here

#Main code flow

$Applications = Get-Applications

Show-ApplicationsForUninstall $Applications

$appnum = Get-AppNumberFromUser($Applications.Count)

if ($appnum -ne 'Q') {
	Uninstall-Application $Applications[$appnum-1] # arrays are zero based

Max, that looks good. Yours does seem better, I was along way off :frowning:

So what are the powershell rules? Put everything in functions and call them at the end ? Is there and guidelines to help me ?

Functions need to be defined before they can be called.

So the function definitions go before any running code.

I typically put my “running code” in a function at the top of the script, put all the other functions after that.

At the very bottom, I call the function with my running code.

YTMV (your tastes may vary)

My code was suggested to not conform to powershell rules…

  1. verb-noun notation
  2. get function should get something, show function show something and so on :slight_smile:
  3. minimize usage of write-host
  4. user interaction implemented as separate code (if it not standard confirmation)

and it not only powershell, but universal good coding style
this add a lot reusability and show clearly what you do, especially if you read your code after few years :slight_smile:

for example if you not need uninstall but installation report you can

  1. rewrite completely, using copy paste and catch bugs because you use global variables or multipurpose functions
  2. import-module myapplicationmanagement ; get-applications | export-csv , get-applications | export-html and even
    send-mailmessage -body (get-applications | select displayname, installationdate )

I don’t remember where I read rules all compiled, may be in some PS book, but it for your own convenience :slight_smile:

Thanks Max, that’s very useful. Appreciate it.