Author Posts

October 20, 2015 at 11:15 am

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]$'

     )
     Clear-host

     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 = $_ } ;
    $option++
    }    
   }
    Catch {
    write-error $_.exception.message
   } 
}

Write-Host "================ $Title ================"
#do

     Uninstall-Applications
     $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')

October 20, 2015 at 11:45 am

First things first ...
What are you attempting with ...

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

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

October 20, 2015 at 11:47 am

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.

October 20, 2015 at 12:39 pm

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?

October 20, 2015 at 1:03 pm

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.

October 21, 2015 at 12:20 am

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 } ;
    $option++
    } 

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

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

   $selection.Uninstallstring

Any thoughts ?

October 21, 2015 at 6:13 am

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 } {
$option++
[PSCustomObject] @{ Option = $option; '-' = ":" ;Displayname = $_ } ;

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

October 21, 2015 at 6:28 am

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

(sorry for the daft question)

October 21, 2015 at 6:46 am

Is this a local scope $script:Table ?

October 21, 2015 at 7:48 am

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.

October 21, 2015 at 7:54 am

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.

October 21, 2015 at 8:01 am

Thanks Mark, appreciate your help.

October 22, 2015 at 12:03 am

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)
		$number++
	}
}

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
	$userinput
}

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
}

October 22, 2015 at 9:50 am

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

October 22, 2015 at 9:57 am

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

October 22, 2015 at 10:41 am

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)

October 22, 2015 at 11:50 am

My code was suggested to not conform to powershell rules...

October 23, 2015 at 6:55 am

1. verb-noun notation
2. get function should get something, show function show something and so on 🙂
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 🙂

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 🙂

October 23, 2015 at 6:57 am

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