none
PowerShell - How to return a number of characters from the right side of a string

    Question

  • Hi,

    I need to extract the right 38 characters of a string and assign the result to a variable. I'm attempting to extract the GUID out of the example string below. I have done some searching but haven't found a clear example so far.

    MsiExec.exe /X{CC41B2F4-7448-4908-9836-6550EA3A2452}

    and

    MsiExec.exe /I{09672816-7915-EE76-B099-988C6364B44D}

    Thanks for your help!

    Rob


    • Edited by robwm1 Thursday, March 16, 2017 11:49 PM
    Thursday, March 16, 2017 10:54 PM

Answers

  • Get-ItemProperty $uninstallPath\* |  where {$_.DisplayName -eq $software} | Select-Object -Expand UninstallString

    \_(ツ)_/

    • Marked as answer by robwm1 Friday, March 17, 2017 11:06 PM
    Friday, March 17, 2017 4:25 PM

All replies

  • One of the more simpler ways could be a regex like this:

    'MsiExec.exe /X{CC41B2F4-7448-4908-9836-6550EA3A2452}' -match '({(.+)})'
    $matches[1]
    $matches[2]

    ... but what for do you need this?  There might be a better way to achieve what you want


    Grüße - Best regards

    PS:> (79,108,97,102|%{[char]$_})-join''

    Thursday, March 16, 2017 11:49 PM
  • I am trying to obtain the GUID for software installed on a client computer and then build out my own command line to remove the software. Using the UninstallString value itself is not always reliable because some of them have msiexec.exe /I which is install, not uninstall.

    My idea is to get just the GUID and then build the following command line:

    msiexec.exe /x {CC41B2F4-7448-4908-9836-6550EA3A2452} /qn

    If there is a better way where you can get the GUID for a software title, it could simplify the whole approach. I am trying to get the value from these registry paths:

    HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall

    HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall

    After doing some research, I found that it is highly recommended to avoid using Win32_Product.

    Thursday, March 16, 2017 11:55 PM
  • Hmmm ... ok ... but why would you need to figure out the GUID by a script? Don't you know before what software do you need to uninstall?

    Did you try the code I posted?


    Grüße - Best regards

    PS:> (79,108,97,102|%{[char]$_})-join''

    Friday, March 17, 2017 12:14 AM
  • Get all Guids:

    $guids = dir HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall|Select @{n='GUID';e={($_.Name -split '\\')[6]}}


    \_(ツ)_/

    • Proposed as answer by jrv Friday, March 17, 2017 5:58 PM
    Friday, March 17, 2017 12:52 AM
  • Hi Rob,

    I think you can use substring method,

    Substring(start position, number of character)

    Based on the examples given by you, assume that the length of the string is always the same,

    Friday, March 17, 2017 12:56 AM
  • $displayName = 'HP ePrint SW'
    Get-ChildItem HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall |
    	?{$_.GetValue('DisplayName') -eq $displayName} |
    	%{$_.GetValue('UninstallString')}


    \_(ツ)_/

    Friday, March 17, 2017 1:02 AM
  • P.S.  - This is the uninstall command it does not need to be changed. Just run ti.

    MsiExec.exe /I{09672816-7915-EE76-B099-988C6364B44D}

    Some uninstallers automatically uninstall with /U and other will use /X.  The "UninstallString" key is correct as is.


    \_(ツ)_/


    • Edited by jrv Friday, March 17, 2017 1:05 AM
    Friday, March 17, 2017 1:04 AM
  • P.S.  - This is the uninstall command it does not need to be changed. Just run ti.

    MsiExec.exe /I{09672816-7915-EE76-B099-988C6364B44D}


     ... hmmm  ...  if I'm not wrong that's actually the install string. The uninstall parameter for msi should be the "/X". If you wnat to make it completely silent you can add "/qn". If you want to show at least a progress bar but without a cancel button you can add "qb-!"

    But what I meant before: When I create a SCCM package with install and uninstall option I usualy know before what product I want to uninstall and so I don't need to determine the product ID with a script. I place the ready to use command line to the script including the right product ID / GUID.


    Grüße - Best regards

    PS:> (79,108,97,102|%{[char]$_})-join''

    Friday, March 17, 2017 7:15 AM
  • Depends on the installer.  Some MSIs will uninstall with that if the product is already installed.  What is missing is the need to specify quiet mode which can be added.

    If I scan on W10 about 1/3 of the uninstaller use /I and when run try to un9nstall the product.  If there is an uninstall key it is correct or Windows could not use it.


    \_(ツ)_/

    Friday, March 17, 2017 7:48 AM
  • In my experiences the uninstall string from the registry often just starts a setup interface where you can chose to install, repair, change or uninstall. All the silent or automatic uninstalls I did until now worked quite well with the parameter "/x".

    Windows Installer Command-Line Options


    Grüße - Best regards

    PS:> (79,108,97,102|%{[char]$_})-join''

    Friday, March 17, 2017 9:28 AM
  • Only saying that with the /I option just add "/qn" and it will work or, if you are worried do this:

    'MsiExec.exe /I{09672816-7915-EE76-B099-988C6364B44D}' -replace '/i|/x', '/qn /x'


    \_(ツ)_/

    Friday, March 17, 2017 3:36 PM
  • To all,

    Thanks for the fast response to my question! I have a lot to try out here.

    I will try out some of your ideas here and report back with what works best.

    -Rob

    Friday, March 17, 2017 3:44 PM
  • jrv,

    Using this command:

    $software = "CustomInvite"
    $uninstallPath = "HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall"

    Get-ItemProperty $uninstallPath\* |  where {$_.DisplayName -eq $software} | Select-Object UninstallString

    The output looks like this:

    @{UninstallString=MsiExec.exe /X{CC41B2F4-7448-4908-9836-6550EA3A2452}}

    I also recall seeing output like this:

    UninstallString
    --------------------
    MsiExec.exe /X{CC41B2F4-7448-4908-9836-6550EA3A2452}

    (I can't remember what I used for this output)

    I must be using the wrong command here. This why I initially asked to extract the GUID so I could drop the rest. I'll try some other things to get a better result.

    Rob

    Friday, March 17, 2017 4:04 PM
  • Get-ItemProperty $uninstallPath\* |  where {$_.DisplayName -eq $software} | Select-Object -Expand UninstallString

    \_(ツ)_/

    • Marked as answer by robwm1 Friday, March 17, 2017 11:06 PM
    Friday, March 17, 2017 4:25 PM
  • So if I do this:

    $software = "CustomInvite"
    $uninstallPath = "HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall"

    $result = Get-ItemProperty $uninstallPath\* |  where {$_.DisplayName -eq $software} | Select-Object -Expand UninstallString

    $cmdLine = "$result /qb"

    cmd /c $cmdLine

      The program uninstalls but I would like PowerShell to wait for this command to complete before it continues. What is the best way to do that? I have tried Invoke-Command and Invoke-Expression but haven't found the right recipe yet.

    Friday, March 17, 2017 5:56 PM
  • Many installers cannot be waited on.

    To launch

    . $cmdLine


    \_(ツ)_/

    Friday, March 17, 2017 6:00 PM
  • That doesn't work:

    . : The term 'MsiExec.exe /X{CC41B2F4-7448-4908-9836-6550EA3A2452} /qb' is not recognized as the name of a cmdlet, function, script file, or operable program.
    Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
    At G:\Software\_Snippets\Get-SoftwareGUID.ps1:49 char:3
    + . $cmdLine
    +   ~~~~~~~~
        + CategoryInfo          : ObjectNotFound: (MsiExec.exe /X{...50EA3A2452} /qb:String) [], CommandNotFoundException
        + FullyQualifiedErrorId : CommandNotFoundException

    .\$cmdLine doesn't work either

    • Edited by robwm1 Friday, March 17, 2017 6:12 PM
    Friday, March 17, 2017 6:11 PM
  • Then use:

    & $cmdLine.

    or this

    cmd /c $cmdLine

    or this:

    $exe, $arg = $result -split ' '
    Start-Process $exe "$arg /qb"


    \_(ツ)_/


    • Edited by jrv Friday, March 17, 2017 6:18 PM
    Friday, March 17, 2017 6:18 PM
  • Read this article.

    Windows IT Pro: Running Executables in PowerShell


    -- Bill Stewart [Bill_Stewart]

    Friday, March 17, 2017 6:38 PM
    Moderator
  • Bill,

    That was a great read! Thank you!

    Rob

    Friday, March 17, 2017 11:00 PM
  • jrv,

    Here is the solution that I will go with pasted below. I found that running msiexec.exe /I did not uninstall anything that I tried it on so I added the -replace switch as you suggested to make it a /X before running. This may seem like a waste to be a function but I will be adding custom logging output to it and include this in my snippet library.

    Function Get-SoftwareUninstallPath ($softwareTitle,$uninstallPath) {
    <#
    .SYNOPSIS
    Given the registry uninstall path and software title, the function returns the UninstallString for the software title passed in.
    
    .DESCRIPTION
    This function receives the name of a software title and registry uninstall path and returns the UninstallString from the registry location specified:
    
    HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall
    or
    HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall
    
    .EXAMPLE
    $result = Get-SoftwareUninstallPath $software $path
    
    .PARAMETER $softwareTitle
    This value is used to filter the search for a specific software title.
    
    .PARAMETER $uninstallPath
    Set value to the registry path where the software will be found.
    
    HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall
    or
    HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall
    
    #>
    
    #Declare variables
    $result = ""
    
    #Get the software's uninstall string
    $result = Get-ItemProperty $uninstallPath\* |  where {$_.DisplayName -eq $softwareTitle} | Select-Object -expand UninstallString
    
    #Return the uninstall string
    return $result
    }
    
    #Declare variables
    $software = "CustomInvite"
    $path = "HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall"
    
    #Get the software uninstall path from the registry
    $returnedValue = Get-SoftwareUninstallPath $software $path
    
    #Change msiexec.exe /I to msiexec.exe /X
    $returnedValue = $returnedValue -replace '/I', '/X'
    
    #Remove software
    & cmd /c $returnedValue /qb

    Thanks for all of your help! (You really should write a book)

    Rob

    Friday, March 17, 2017 11:05 PM
  • ?I only works on packages designed that way.  On most others it will just do nothing if the /qn switch is used.

    \_(ツ)_/

    Friday, March 17, 2017 11:07 PM
  • Here is one from the Gallery here.

    \_(ツ)_/

    Friday, March 17, 2017 11:09 PM
  • robwm1,

    so you obviously know already what software product you like to uninstall. If this software does not exist in different versions in your environment you don't have to determine the uninstall string or the Product-ID / GUID with the script. You already know it before. So you just have to run the uninstall command and the issue is over. ;-) 

    Really simple like this:

    Start-Process -FilePath 'msiexec.exe' -ArgumentList '/x{CC41B2F4-7448-4908-9836-6550EA3A2452} /qn'

    Or - if you want to have it a little more sofisticated like this:

    $ProductID = '{CC41B2F4-7448-4908-9836-6550EA3A2452}'
    $Result = (Start-Process -FilePath 'msiexec.exe' -ArgumentList "/x$ProductID /qn" -Wait -Passthru).ExitCode

    Now you even have the exit code of the msi process and can check if it's been ok.

    Or - if you're really curious you could tell the msi to write an advanced logfile like this:

    $ProductID = '{CC41B2F4-7448-4908-9836-6550EA3A2452}'
    $MSILogFile = 'C:\logfile.log'
    $Result = (Start-Process -FilePath 'msiexec.exe' -ArgumentList "/x$ProductID /l*v '$MSILogFile' /qn" -Wait -Passthru).ExitCode

    Sometimes it happens that even msi installers include custom actions who trigger an unwanted reboot. Then you can try something like this:

    $ProductID = '{CC41B2F4-7448-4908-9836-6550EA3A2452}'
    $MSILogFile = 'C:\logfile.log'
    $Result = (Start-Process -FilePath 'msiexec.exe' -ArgumentList "/x$ProductID REBOOT=ReallySuppress /l*v '$MSILogFile' /qn" -Wait -Passthru).ExitCode


    Grüße - Best regards

    PS:> (79,108,97,102|%{[char]$_})-join''


    • Edited by BOfH_666 Saturday, March 18, 2017 1:32 PM
    Saturday, March 18, 2017 1:31 PM
  • Here is an Uninstall string:

    "C:\ProgramData\Caphyon\Advanced Installer\{5E951FA8-E127-4C74-AB27-BBD3698E013E}\PHW17Setup_2.1.34.0_XXXXXX_x64.exe /x {5E951FA8-E127-4C74-AB27-BBD3698E013E} AI_UNINSTALLER_CTP=1"

    It is composed of two parts:

    $installer ="C:\ProgramData\Caphyon\Advanced Installer\{2CDC80D4-C319-40EC-8641-88A72E28548D}\SPS17Setup_5.4.136.0_XXXXXX_x64.exe"

    And:

    $parameters = "/x {2CDC80D4-C319-40EC-8641-88A72E28548D} AI_UNINSTALLER_CTP=1"

    Note that many, if not most, installer on Windows Vista and later are custom packages.  They cannot use MsiExec correctly in many cases.

    Most third party software and much of Microsoft Software uses these custom installer packages.  Each installer vendor may take a different approach to an uninstall.  Using MsiExec against many of these packages will not work which is why there is an UnInstallString entry.

    MsiExec is still being used but, as you can see above, other options are needed by the bootstrapper.

    If MsiExec is used against the correct GUID then it will try to call the UnInstallString value but the product GYUD found without an uninstall string may only do a partial removal of a product.

    Here is one of many installer vendors that provide MSI based installers: Caphylon Ltd. Advanced Installer

    Here is an InstallShield registry setting:

    InstallShield_{E1646825-D391-42A0-93AA-27FA810DA093}

    There is no UninstallString.

    Here is an Uninstall string witj no GUID in sight:

    rundll32.exe "C:\Program Files\Synaptics\SynTP\SynISDLL.dll",standAloneUninstall

    So be careful how you manage uninstall strings.


    \_(ツ)_/




    • Edited by jrv Saturday, March 18, 2017 5:31 PM
    Saturday, March 18, 2017 5:19 PM
  • PowerShell to find keys in both registries and match all or part of string.

    $keys = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*',
    		'HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall'
    Get-ItemProperty $keys| Select-Object WindowsInstaller, DisplayName, UninstallString
    
    # OR get only MSIExec packages
    Get-ItemProperty $keys | Where{$_.WindowsInstaller } | 
    	Select-Object WindowsInstaller, DisplayName, UninstallString |
    	Format-Table  @{ label = 'Win'; expression = { $_.WindowsInstaller };w=3},
    		@{ label = 'x'; expression = { $_.DisplayName } },
    		@{ label = 'Uninstall'; expression={ $_.UninstallString } }
    
    # OR Get a package by name
    $DisplayName = 'SQL Server 2016 Batch Parser'
    Get-ItemProperty $keys | 
    	Where-Object{ $_.WindowsInstaller  -and $_.DisplayName -match $DisplayName} |
    	Select-Object WindowsInstaller, DisplayName, UninstallString 
    
    	


    \_(ツ)_/


    • Edited by jrv Saturday, March 18, 2017 6:19 PM
    Saturday, March 18, 2017 6:19 PM