[scripting] How do you execute an arbitrary native command from a string?

I can express my need with the following scenario: Write a function that accepts a string to be run as a native command.

It's not too far fetched of an idea: if you're interfacing with other command-line utilities from elsewhere in the company that supply you with a command to run verbatim. Because you don't control the command, you need to accept any valid command as input. These are the main hiccups I've been unable to easily overcome:

  1. The command might execute a program living in a path with a space in it:

    $command = '"C:\Program Files\TheProg\Runit.exe" Hello';
    
  2. The command may have parameters with spaces in them:

    $command = 'echo "hello world!"';
    
  3. The command might have both single and double ticks:

    $command = "echo `"it`'s`"";
    

Is there any clean way of accomplishing this? I've only been able to devise lavish and ugly workarounds, but for a scripting language I feel like this should be dead simple.

This question is related to scripting powershell

The answer is


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.


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

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