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

Johnny Kauffman picture Johnny Kauffman · Jun 14, 2011 · Viewed 264.2k times · Source

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.

Answer

Joel B Fant picture Joel B Fant · Jun 14, 2011

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.