PowerShell V3 CTP2 Provides Better Argument Passing to EXEs

Within PowerShell it has always been easy to pass “simple” arguments to an EXE e.g.:

C:\PS> ipconfig -all

However passing arguments to certain exes can become surprising difficult when their command line parameter syntax is complex i.e. they require quotes and use special PowerShell characters such as @ $ ;.  A lot of these problems can be solved by placing single or double quotes in the right places or by escaping PowerShell’s special characters e.g.:

C:\PS> tf.exe status . /workspace:HILLR1;hillr /r
There are no pending changes.
The term 'hillr' 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:35
+ tf.exe status . /workspace:HILLR1;hillr /r
+                                   ~~~~~
    + CategoryInfo          : ObjectNotFound: (hillr:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

Note that in the command line above the “/workspace” parameter value is specified using a special syntax that TF.exe recognizes i.e. <workspace_name>;<username>.  Unfortunately the semicolon is a statement separator in PowerShell which means that TF.exe only sees the parameters before the semicolon.  We can use the ECHOARGS.exe utility from the PowerShell Community Extensions to verify this:

C:\PS> echoargs.exe status . /workspace:HILLR1;hillr /r
Arg 0 is <status>
Arg 1 is <.>
Arg 2 is </workspace:HILLR1>

In this case, the solution is simple – just escape the semicolon e.g.:

C:\PS> tf.exe status . /r /workspace:HILLR1`;hillr
File name     Change Local path
------------- ------ -----------------------------------------
$/Foo/Trunk/Tools/Bin
TfsTools.psm1 edit   C:\Tfs\Foo\Trunk\Tools\Bin\TfsTools.psm1

1 change(s)

This works up to the point where you get quite frustrated figuring out which characters to escape and which parameter/argument pairs need to be quoted and whether you should use single quotes or double quotes.  Fortunately, it looks like we will get a way to tell the PowerShell argument parser to stop doing so much work for us and just pass the args through “as-is”.  In other words, you can tell PowerShell to become a “dumber” command line parser.  This mode is invoked using the character sequence: –% and it works from the point it appears on the command line to the end of that line.  Note that the character sequence may change or the feature could be completely removed before V3 ships.

Given this new feature, here’s how you use it.  Take this example of a problematic set of command line parameters:

C:\PS> sqlcmd -S .\SQLEXPRESS -v lname="Gates" -Q "SELECT FirstName,LastName FROM
AdventureWorks.Person.Contact WHERE LastName = '$(lname)'"
The term 'lname' 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:126
+ ...  LastName = '$(lname)'"
+                    ~~~~~
    + CategoryInfo          : ObjectNotFound: (lname:String) [], CommandNotFou
   ndException
    + FullyQualifiedErrorId : CommandNotFoundException

In this case the V2 solution is to escape the $ character in the last part of the command line e.g.: ‘`$(lname)’ but if you don’t want to spend the time to figure this out you can easily use –% like so:

C:\PS> sqlcmd --% -S .\SQLEXPRESS -v lname="Gates" -Q "SELECT FirstName,LastName F
ROM AdventureWorks.Person.Contact WHERE LastName = '$(lname)'"
FirstName                                          LastName

---------------------------------- -----------------------------------
Janet                              Gates

(1 rows affected)

You can put the –% later in the parameter list if you want.  You might want to do this if you need to use PowerShell variable expansion in some of the arguments.  Just note that once you specify –% the rest of the command line will be parsed “dumbly”.  You will get no PowerShell variable expansion or grouping expressions and you won’t be able to escape newlines.  One thing you can do in this special parsing mode is expand environment variables using the batch syntax of %ENV_VAR% e.g.:

C:\PS> $env:colname = "LastName"
C:\PS> sqlcmd -S .\SQLEXPRESS -v lname="Gates" --% -Q "SELECT FirstName,LastName F
ROM AdventureWorks.Person.Contact WHERE %colname% = '$(lname)'"
FirstName                                          LastName

---------------------------------- -----------------------------------
Janet                              Gates

(1 rows affected)

I believe this new command line parsing feature will greatly simplify interacting with exes that have a complex command line parameter syntax.  Thanks to the PowerShell team for listening to the community feedback on this issue and providing a solution.

About these ads
This entry was posted in PowerShell 3.0 and tagged , , . Bookmark the permalink.

8 Responses to PowerShell V3 CTP2 Provides Better Argument Passing to EXEs

  1. Martin Zugec says:

    Nice catch, didn’t know about this parsing – can be pretty handy!

  2. Richard says:

    pity the – – is showing up as a em-dash in my browser ;-(

  3. Pingback: Simple Powershell Msbuild with parameter fails | PHP Developer Resource

  4. Pingback: PowerShell V3 Featured Articles (en-US)

  5. Lionel says:

    Is there a documentation of this “dumber” command parsing? If %ENV_VAR% is expanded, this means that some escape sequences are required (e.g. if I want %ENV_VAR% literally, not expanded), but which ones exactly?

    By the way, it looks like your echoargs.exe echoes the arguments to main() one by one. This is helpful for debugging (I wrote the same thing myself), but you have included one unnecessary layer of command processing. Windows does not pass an array of aguments to a new process, it passes a single string, the command line. Splitting this command line into arguments for main() is usually done by the C runtime library, but not always, and even the C runtime has at least two modes of command line pasing. If you want a really low level, full fidelity view of what Powershell is doing, it would be better to print the output of GetCommandLineW().

    • rkeithhill says:

      The latest version of echoargs (in PSCX 3.0 beta) does print out the entire command line as a single string. The only documentation I’ve seen on this feature is a brief mention in this PowerShell Team blog post. As far as escaping the % goes, I’m not sure there’s a way to do that however you can list some parameters before the –% and some after.

  6. Pingback: Neue Features in PowerShell Version 3.0 – Beta - PowerShell Get-Script -Name Peter Kriegel | Management

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s