April 2, 2012

Top 5 tips for running external commands in Powershell

Running an external command in powershell looks pretty easy, to run notepad just do :


Things get a little be more complicated when you add many arguments with spaces and quotes, something like the following just wont work :

Winrm set winrm/config/listener?Transport=HTTP+Address=* @{Enabled="false"}

Why is that ?

Powershell is a programming language, it has to evaluate (interpret) every line in a script as an expression. The preceding line is not a valid Powershell expression. Each arguments must be treated as an object (a String in this case), So a valid expression would be :

&Winrm "set" "winrm/config/listener?Transport=HTTP+Address=*" "@{Enabled=`"false`"}"

As you can see this can rapidly become a nightmare when you add variables and path to that call. Really not easy to debug. This is why I wrote my top 5 tips for running external commands in Powershell.

Tip #1 : Use the ampersand call operator (&)

Using the call operator (&) will help Powershell to evaluate the line as a command and not as an object. For example it will allow the following command to start Internet Explorer :

&"C:\Program Files\Internet Explorer\iexplore.exe"

Tip #2 : Do not put all arguments in one string

Each string will be treated one argument. Put each argument in a different string et separate them with a space or even better put them in an array like this :

$CallArgs = @("set",
&WinRM $CallArgs

Tip #3 : Don’t forget to escape each quote (")

If you want a quote to appear in an argument you must escape it like this : `"

Tip #4 : Use EchoArgs.exe for debugging

Debugging the call operator can be pretty hard simply because you can’t see what is really passed as arguments to the external command by Powershel. EchoArgs.exe is a great little tool that will simply display the arguments and the command line used to call the program. So the following command :

&.\echoargs.exe winrm "set" "winrm/config/listener?Transport=HTTP+Address=*" "@{Enabled=`"false`"}"

Will produce :

Arg 0 is <winrm>
Arg 1 is <set>
Arg 2 is <winrm/config/listener?Transport=HTTP+Address=*>
Arg 3 is <@{Enabled=false}>

Command line:
"I:\Mes Documents UL\PoSH\EchoArgs.exe"  winrm set winrm/config/listener?Transport=HTTP+Address=* @{Enabled="false"}

EchoArgs.exe is part of the Powershell Community Extensions (PSCX). You should download the version 2.1 (still in beta) to get the full comand line displayed. The Arguments (arg 0, arg 1, …) displayed by EchoArgs.exe seems to have been normalized (quotes have bnne strip), so the full command line is very helpfull.

Tip #5 : Use dot-sourcing

Don’t forget to use Powershell dot-source syntax (.\) when you call an external command in the current directory :



JosefZ said...

Please do not confuse others by mistaking
  - a dot-source operator (a dot followed by a space), and
  - a dot representing current directory (a dot followed by a backslash).

Dot-sourced command should read as follows:
. .\echoargs.exe

ParadisJ said...

Thanks for the remark JosefZ.
Your are absolutely right, &.\echoargs.exe is not a 'dot source' call.
In my many version of this post I guest this one must have slip.

Anyway, in fact I should completely remove Tip #5 because you can't use dot sourcing with the call operator (&).

Post a Comment