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.
Nice catch, didn’t know about this parsing – can be pretty handy!
pity the – – is showing up as a em-dash in my browser ;-(
Yeah I tried to fix this in WordPress but haven’t found a way yet to prevent WordPress from doing this. It shows up correctly in Windows Live Writer.
Pingback: Simple Powershell Msbuild with parameter fails | PHP Developer Resource
Pingback: PowerShell V3 Featured Articles (en-US)
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().
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.
Pingback: Neue Features in PowerShell Version 3.0 – Beta - PowerShell Get-Script -Name Peter Kriegel | Management
Pingback: Is there a excess in a emigration format Add x to y? - Zuwkowski Gyan
What about single quotes instead?
You can use single quotes but if the string contains embedded single quotes you have to double up e.g. ”’hi”’ to get ‘hi’