Uninstall program using get-process and uninstallstring with call operator (&) - powershell

I'm trying create a uninstall script to uninstall any program tha I want.
I was using this script:
get-package -Name "XXXXX" | Uninstall-Package
But I saw that when the "provideName" attribut is "Program" it not working, so I use this.
$packageArray = get-package "*XXXX*"
foreach ($package in $packageArray){
$a = $package.ProviderName
if ($a -eq "Programs"){
& {($package.Meta.Attributes["UninstallString"] -replace '"') /S}
}
if ($a -ne "Programs"){
get-package -Name "*XXXXX*" | Uninstall-Package
}
}
This part bellow was working fine when I have not ""&quot."", like this.
& {($package.Meta.Attributes["UninstallString"] -replace '"') /S}
Print:
But now I'm getting erro when a uninstall string has "&quot." value, like this.
UninstallString=""&quot";C:\Users\rsantanna\AppData\Local\GoToMeeting\19950\G2MUninstall.exe" /uninstall"
Print.
When it occur I get this error.
& : The term 'C:\Users\rsantanna\AppData\Local\GoToMeeting\19950\G2MUninstall.exe /uninstall' 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 line:6 char:7
& ($UninstallString)
~~~~~~~~~~~~~~~~~~
CategoryInfo : ObjectNotFound: (C:\Users\rsanta....exe /uninstall:String) [], CommandNotFoundException
FullyQualifiedErrorId : CommandNotFoundException
Anyone can help me ?

You cannot directly execute a string expression such as ($package.Meta.Attributes["UninstallString"] -replace '"')
While you can do so via &, the call operator, it only works if the string expression evaluates to a command name or executable file path only.
Notably, you can not use & to execute an entire command line, which is what $package.Meta.Attributes["UninstallString"] typically contains.
Note that your use of & is with a script block ({ ... }), which allows you to execute arbitrary statements contained in the block; however, perhaps needless to say, these statements are subject to the usual syntax rules, and it is the one and only statement in your block that causes a syntax error (that is actually different from the error you report).
As explained in detail in this answer, the command lines stored in UninstallString registry values are designed for invocation from cmd.exe or from no-shell environments.
Therefore, the simplest solution is to call via cmd.exe /c:
cmd.exe /c "$($package.Meta.Attributes["UninstallString"]) /S"
If your uninstall command line, as returned from $package.Meta.Attributes["UninstallString"], really contains " in lieu of verbatim " characters (double quotes) - which would be unusual - you'll need to replace the former with the latter first:
cmd.exe /c "$($package.Meta.Attributes["UninstallString"] -replace '"', '"') /S"
It's unclear what command produced the " output in your screenshot, but given that it shows the text of an XML document, the use of " instead of " is simply a necessity for encoding literal " chars.

Related

Why a powershell command is valid in the terminal but invalid from lnk file?

For example, this command works perfectly from a PowerShell prompt :
powershell.exe -NoExit -c Set-Variable -Name "CA_HOME" -Value "$(Get-Location).Path\intermed-ca"
but fails with an error if used from an lnk file.
Set-Variable : A positional parameter cannot be found that accepts argument '\intermed-ca'.
At line:1 char:1
+ Set-Variable -Name CA_HOME -Value $(Get-Location).Path'\intermed-ca'
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [Set-Variable], ParameterBindingException
+ FullyQualifiedErrorId : PositionalParameterNotFound,Microsoft.PowerShell.Commands.SetVariableCommand
Why ?
The error you supplied is not from that particular command. Notice PowerShell is showing us the command it is erroring on.
That particular error is simply a syntax error; that is not how you concatenate strings. Running just $(Get-Location).Path'\intermed-ca' will give you an error: "Unexpected token ''\intermed-ca'' in expression or statement."
Going back to the command you posted at the top of the OP, that is proper syntax, but won't be the Path you're looking for because the logic is flawed. So, what you posted as your command will not generate an error, but will return a path that likely doesn't exist. Assuming your working directory is your user profile, you will get something like this back for $CA_HOME: C:\Users\Zulgrib.Path\intermed-ca. The reason is that you're basically doing this:
$path = (Get-Location).Path
'{0}.Path\intermed-ca' -f $path
The .Path is part of the string, not the string execution. That's why you should add parenthesis to ensure that this property is returned as part of the string execution:
powershell.exe -NoExit -c Set-Variable -Name "CA_HOME" -Value "$((Get-Location).Path)\intermed-ca"
While this is fine and will work without issues, quote translations can be tricky from the command line. So, this really isn't a perfect solution for all scenarios. For better compatibility, I would get in the habit of wrapping the whole command in double quotes and keep single quotes inside the command:
powershell.exe -NoExit -c "Set-Variable -Name 'CA_HOME' -Value ('{0}\intermed-ca' -f (Get-Location).Path)"
For the best compatibility, consider base64 encoding your command and running it with -EncodedCommand. See the very bottom of powershell.exe /? for an example. Be weary that some situations will run into issues with overly long CLI commands.
Note: '{0}\intermed-ca' -f (Get-Location).Path and '{0}\intermed-ca' -f (Get-Location) will give the same result. When PowerShell knows it's injecting a PSOobject into a string, it'll give you the string form of what you likely want.

PowerShell Error - Missing expression after unary operator '-'

I have two PowerShell scripts. One is for uninstalling and installing a SharePoint 2010 solution on the SharePoint server. The other calls this script with two commands, one for the uninstall and one for the install. These are not scripts I wrote, but I have inherited them.
Here is the script that calls the install / uninstall script (I've removed some parameters to simplify for troubleshooting):
& 'C:\Users\username\Documents\setup.ps1' -InstallOrUninstall '/UNINSTALL'
& 'C:\Users\username\Documents\setup.ps1' -InstallOrUninstall '/INSTALL'
Stripped down to almost nothing for the purpose of testing, here is the "setup.ps1" script:
param ($InstallOrUninstall,
$SiteURL,
$WebURL,
[switch]$ignoreFeatures,
[switch]$thisAppDomain )
if (-not $thisAppDomain)
{
Write-Host "Invoking script in a new app domain" -foregroundcolor yellow
Write-Host $MyInvocation.Line
powershell.exe -Version 2 -Command $MyInvocation.Line -thisAppDomain
return;
}
Write-Host "In Body"
Write-Host $MyInvocation.Line
Running the first script returns an error from the first command, but not from the second. The error is:
powershell.exe : - : Missing expression after unary operator '-'.
At C:\Users\username\Documents\setup.ps1:11 char:6
+ powershell.exe -Version 2 -Command $MyInvocation.Line -thisAppDo ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (- : Missing exp...y operator '-'.:String) [], RemoteException
+ FullyQualifiedErrorId : NativeCommandError
+ CategoryInfo : ParserError: (-:String) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : MissingExpressionAfterOperator
I believe the reason the install / uninstall script is resending the command for "Version 2" is because of an issue with using PowerShell past Version 2 with SharePoint 2010 (as outlined here). However, I don't understand why only the first line fails. The second command also enters the if statement, but does not error.
If I remove the second line, and only call the setup.ps1 one time, the script that calls the install / uninstall script succeeds.
A nice little brain-teaser. Apparently, $MyInvocation.Line contains the full line, including the newline / linebreak at the end. So, -thisAppDomain is not interpreted as a parameter, but the beginning of a new expression starting with -. This is also, why it works if you remove the 2nd line, because then you do not have a line break at the end.
To reproduce this error, try:
powershell.exe -Version 2 -Command "`r`n-thisAppDomain"
Note that in newer versions the parsing algorithm was obviousy modified and the error message is different. Omit the -Version 2 switch and you likely get:
The term "-thisAppDomain" is not recognized as the name of a cmdlet, function, script file, or operable program...
One simple way to resolve this would be to .Trim() (or .TrimEnd()) the command:
powershell.exe -Version 2 -Command $MyInvocation.Line.Trim() -thisAppDomain
I need to add though, that you should reconsider what your actual problem is, and if your solution is actually the best way to solve it. Have a look at jobs for example.
As marsze said: It is not interpreted as a parameter, but the beginning of a new expression starting with -. This is also, why it works if you remove the 2nd line, because then you do not have a line break at the end.

Set window title when running "start powershell" in powershell not working?

The following command works in CMD (How to start powershell with a window title?).
start powershell -NoExit -command "$Host.UI.RawUI.WindowTitle = 'bits'"
But it doesn't work in Powershell.
PS C:\> start powershell -noexit -command "$Host.UI.RawUI.WindowTitle = 'test'; read-host"
Start-Process : A parameter cannot be found that matches parameter name 'noexit'.
At line:1 char:18
+ start powershell -noexit -command "$Host.UI.RawUI.WindowTitle = 'test ...
+ ~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [Start-Process], ParameterBindingException
+ FullyQualifiedErrorId : NamedParameterNotFound,Microsoft.PowerShell.Commands.StartProcessCommand
The following command can open a new powershell window.
start powershell "$Host.UI.RawUI.WindowTitle = 'test'; read-host"
However, the new window shows the following error message and the title is not set.
System.Management.Automation.Internal.Host.InternalHost.UI.RawUI.WindowTitle : The term
'System.Management.Automation.Internal.Host.InternalHost.UI.RawUI.WindowTitle' 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 line:1 char:1
+ System.Management.Automation.Internal.Host.InternalHost.UI.RawUI.Wind ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (System.Manageme...wUI.WindowTitle:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException
Bacon Bits' helpful answer explains that start in cmd.exe means something different than in PowerShell.
Use Start-Process as follows to get the desired result; note that powershell implicitly binds to parameter -FilePath, whereas the ,-separated arguments starting with -NoExit bind implicitly to the -ArgumentList (-Args) parameter, which accepts an array of strings:
# In PowerShell, `start` is an alias for `Start-Process`
start powershell '-NoExit', '-command', "`$Host.UI.RawUI.WindowTitle = 'bits'"
In paticular, --prefixed pass-through arguments must be quoted so that they're not mistaken for Start-Process's own parameters.
Also note the ` preceding the $ in $Host, which prevents up-front interpolation of $Host by the calling PowerShell instance.
You could also use '$Host.UI.RawUI.WindowTitle = ''bits''', a single-quoted literal string with embedded single quotes escaped as ''.
Important:
While passing arguments as an array to -ArgumentList is conceptually the best approach, it is unfortunately ill-advised due to a long-standing bug in Start-Process, still present as of this writing (v7.1) - see GitHub issue #5576.
For now, using a single string comprising all arguments, enclosed in embedded "..." quoting as necessary, is the only generally robust approach. As discussed in the linked GitHub issue, an -ArgumentArray parameter that supports robust array-based argument passing may be introduced in the future.
In the case at hand this means the following, as suggested by PetSerAl in a comment on the question:
Start-Process powershell '-NoExit -command "$Host.UI.RawUI.WindowTitle = ''bits''"'
Note the single-quoting_ ('...') of the overall argument-list string, which then necessitates escaping the embedded single quotes - those that PowerShell should see as part of the command - as ''.
In Command Prompt, start is the start internal command. In Windows Powershell, start is an alias for Start-Process, which does something similar but isn't identical.
Try running this:
powershell -NoExit -command "`$Host.UI.RawUI.WindowTitle = 'bits'"
Here's another way, while avoiding the dollar sign. The double quotes have to be on the outside, so that bits stays quoted.
start powershell "-noexit (get-variable host).value.ui.rawui.windowtitle = 'bits'"
You can always avoid these quoting issues putting the command in a file.
start powershell '-noexit .\window.ps1'
start powershell with the command option for setting the window title did not work for me. Maybe because I want to open another powershell with a ps1 file. so in the other ps1 file I added the first line as below,
(Get-Host).ui.RawUI.WindowTitle='TEST TEST'
and it worked like a charm....
If you add this to your powershell profile.ps1 you can get the window title to show the current running script and if you are just opening a window with no script then 'pwsh' will be displayed.
Will be systematic with no need to add a line on top of each script. The other answers
combined with $MyInvocation.MyCommand seem to give the name of the profile.ps1 instead when running a script from the context menu.
This can also be tweaked to change the result.
[console]::title = Split-Path -Leaf ([Environment]::GetCommandLineArgs()[-1]).Replace('pwsh.dll','pwsh')
Works on both PS 5 and 7 . For ver. 5 replace pwsh.dll by powershell.exe

Passing Powershell from Cmd with variable evaluation

I want to use the following command
powershell -command "get-content %CONFIG_FILE% | %{$_ -replace \"APP_PATH=.+\",\"APP_PATH=%APP_DIR%\"}"
but with the evaluation of the %CONFIG_FILE% and the %APP_DIR% which are defined in the batch script using
set CONFIG_FILE=C:\B0_GW.cfg
set APP_DIR=C:\dbg\kernel
when i do so, i currently get the following issue:
The string is missing the terminator: ".
+ CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : TerminatorExpectedAtEndOfString
any ideas?
aschipfl has provided the crucial pointer in a comment on the question:
From within a batch file[1], you must escape % characters that you want to pass through to the target application as %%.
Otherwise, cmd interprets % as the start or end of an environment-variable reference (which in part happens by design in your command; e.g., %CONFIG_FILE%).
(You've already correctly escaped embedded " chars. in your command as \" for PowerShell).
Specifically, the % in the %{...} part of your command needs escaping (% is PowerShell's built-in alias for the ForEach-Object cmdlet):
powershell -command "get-content %CONFIG_FILE% | %% {$_ -replace \"APP_PATH=.+\",\"APP_PATH=%APP_DIR%\"}"
Alternatively, simply use the cmdlet's actual name, ForEach-Object, which obviates the need for escaping:
powershell -command "get-content %CONFIG_FILE% | ForEach-Object {$_ -replace \"APP_PATH=.+\",\"APP_PATH=%APP_DIR%\"}"
[1] Sadly, the behavior at cmd.exe's command prompt (in an interactive session) differs - see this answer.

Powershell | Unable to perform command with space character

Can anyone explain to me why:
iex "C:\Program Files\test\test.exe"
Returns:
C:\Program : The term 'C:\Program' 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 line:1 char:1
+ C:\Program Files\test\test.exe
+ ~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (C:\Program:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException
I've tried to get this working multiple different ways:
Wrapping the text in ()
Putting the string into a variable and passing it as a variable instead
Using single quotes
Using double quotes
I don't know how else I can get it to realize that the entire string must be run, not just the first word.
Post-answered example
The question has been answered. Here is something I was trying to get working:
$tool = "C:\Windows\System32\cmd.exe"
$param = "/c ping google.com -n 1"
$test = & $tool $param
Write-Host $test
It turns out that the line with & does NOT work with double quotes "" in this instance, and actually worked without them. I think this has to do with there being arguments/parameters involved.
Use the & operator together with quotes:
& "C:\Program Files\test\test.exe"
From help about_operators:
& Call operator
Runs a command, script, or script block. The call operator, also known as
the "invocation operator," lets you run commands that are stored in
variables and represented by strings. Because the call operator does not
parse the command, it cannot interpret command parameters.
C:\PS> $c = "get-executionpolicy"
C:\PS> $c
get-executionpolicy
C:\PS> & $c
AllSigned