How do you execute an arbitrary native command from a string?
Invoke-Expression
, also aliased as iex
. The following will work on your examples #2 and #3:
iex $command
Some strings won't run as-is, such as your example #1 because the exe is in quotes. This will work as-is, because the contents of the string are exactly how you would run it straight from a Powershell command prompt:
$command = 'C:\somepath\someexe.exe somearg'
iex $command
However, if the exe is in quotes, you need the help of &
to get it running, as in this example, as run from the commandline:
>> &"C:\Program Files\Some Product\SomeExe.exe" "C:\some other path\file.ext"
And then in the script:
$command = '"C:\Program Files\Some Product\SomeExe.exe" "C:\some other path\file.ext"'
iex "& $command"
Likely, you could handle nearly all cases by detecting if the first character of the command string is "
, like in this naive implementation:
function myeval($command) {
if ($command[0] -eq '"') { iex "& $command" }
else { iex $command }
}
But you may find some other cases that have to be invoked in a different way. In that case, you will need to either use try{}catch{}
, perhaps for specific exception types/messages, or examine the command string.
If you always receive absolute paths instead of relative paths, you shouldn't have many special cases, if any, outside of the 2 above.
Please also see this Microsoft Connect report on essentially, how blummin' difficult it is to use PowerShell to run shell commands (oh, the irony).
http://connect.microsoft.com/PowerShell/feedback/details/376207/
They suggest using --%
as a way to force PowerShell to stop trying to interpret the text to the right.
For example:
MSBuild /t:Publish --% /p:TargetDatabaseName="MyDatabase";TargetConnectionString="Data Source=.\;Integrated Security=True" /p:SqlPublishProfilePath="Deploy.publish.xml" Database.sqlproj
If you want to use the call operator, the arguments can be an array stored in a variable:
$prog = 'c:\windows\system32\cmd.exe'
$myargs = '/c','dir','/x'
& $prog $myargs
The call operator works with ApplicationInfo objects too.
$prog = get-command cmd
$myargs = -split '/c dir /x'
& $prog $myargs
The accepted answer wasn't working for me when trying to parse the registry for uninstall strings, and execute them. Turns out I didn't need the call to Invoke-Expression
after all.
I finally came across this nice template for seeing how to execute uninstall strings:
$path = 'HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall'
$app = 'MyApp'
$apps= @{}
Get-ChildItem $path |
Where-Object -FilterScript {$_.getvalue('DisplayName') -like $app} |
ForEach-Object -process {$apps.Set_Item(
$_.getvalue('UninstallString'),
$_.getvalue('DisplayName'))
}
foreach ($uninstall_string in $apps.GetEnumerator()) {
$uninstall_app, $uninstall_arg = $uninstall_string.name.split(' ')
& $uninstall_app $uninstall_arg
}
This works for me, namely because $app
is an in house application that I know will only have two arguments. For more complex uninstall strings you may want to use the join operator. Also, I just used a hash-map, but really, you'd probably want to use an array.
Also, if you do have multiple versions of the same application installed, this uninstaller will cycle through them all at once, which confuses MsiExec.exe
, so there's that too.