Search

Executing commands which require quotes and variables is practically impossible by Joel Bennett

Closed
as Fixed Help for as Fixed

92
1
Sign in
to vote
Type: Bug
ID: 376207
Opened: 10/17/2008 12:10:35 PM
Access Restriction: Public
5
Workaround(s)
18
User(s) can reproduce this bug
It's FAR TOO HARD to pass parameters to applications which require quoted strings. I asked this question in IRC with a "roomful" of PowerShell experts, and it took hour for someone to figure out a way (I originally started to post here that it is simply not possible).

This completely breaks PowerShell's ability to serve as a general purpose shell, because we can't do simple things like executing sqlcmd. The number one job of a command shell should be running command-line applications...

As an example, trying to use SqlCmd from SQL Server 2008, there is a -v parameter which takes a series of name:value parameters. If the value has spaces in it, you must quote it. For instance, this will work in cmd.exe (it doesn't do anything, just exits silently):
        sqlcmd -v user="Joel Bennett" -Q "select '$(user)' as UserName"

But in PowerShell, it has to be:
        sqlcmd -v user=`"Joel Bennett`" -Q 'select ''$(user)'' as UserName'

Notice that you have to escape things two different ways here? How many people are going to figure that out?

But it gets worse if you need to user a variable (eg, $Env:FullName):
$Env:FullName="Joel Bennett"

So you can execute it in CMD:
     sqlcmd -v user="%FullName%" -Q "select '$(user)' as UserName"

But how many PowerShell users are going to figure out that this (sortof) works:
     sqlcmd -v user=($env:FullName) -Q 'select ''$(user)'' as UserName'

What appears to be happening is that "user=($env:FullName)" is treated as a two part array: "user=" and "Joel Bennett" ... so the engine inserts a space after the "user=" (which I can't get rid of by setting $ofs, oddly) and then inexplicably and magically chooses to wrap quotes around the "Joel Bennett" ....

The problem is, as you can see from these examples, there is no single way to write a command line to invoke this application correctly, so even after you master all 4 or 5 different ways of quoting and escaping things, you're still guessing as to which will work when ... or, you can just shell out to cmd, and be done with it.
Details (expand)
How often does this happen?
Always Happens

Have you seen this problem before in this product?

Yes, this happens in all previous versions
Reproduction Steps
# create a batch file that echoes back to test with
Set-Content EchoTest.cmd "Echo %*"

# try our command and notice it wraps the user= in quotes:
.\echotest.cmd -v user="Joel Bennett"

# Shell through cmd and notice that it doesn't.
cmd /c '.\echotest.cmd -v user="Joel Bennett"
Expected Results
Invoking an executable should be AT LEAST as easy as in DOS.
File Attachments
0 attachments
Sign in to post a comment.
Posted by emission on 12/10/2012 at 2:38 AM
Closed?

Can MS please describe:

1. What you mean by closed.
2. What's so difficult... This seems like shell language 101.
Posted by matthickford on 6/6/2012 at 8:07 AM
"Closed as fix"?

If so, PLEASE link the fix!!!!!!
Posted by AussieJohn on 6/1/2012 at 12:12 AM
If we could call PowerShell directly from SQL Server (SQL Connect 303417) that would help a fair bit with SQL PowerShell quoting issues when going in that direction. To have to call xp_cmdshell (so the old CMD shell) and then PowerShell adds to quoting problems.

In the PowerShell to SQL direction, an easy-to-fix sqlcmd example is the server name. In CMD shell
sqlcmd -S machine\instance -E
works. In PowerShell you have to do
sqlcmd -S 'machine\instance' -E
which may inhibit some DBAs from using PowerShell.
Posted by Microsoft on 12/9/2011 at 4:24 PM
Using the LiteralPath parameter is the pattern we've established for dealing with special characters. We've added LiteralPath to a bunch of cmdlets in PowerShell 3.0. We've also addressed a number of one-off cases in our backtick and square bracket handling. We realize we've not done here, so please continue to file additional specific high-impact cases so we can continue to address them in future releases.
Posted by enndub on 7/15/2011 at 5:04 PM
You can escape double quotes for command line arguments with a backslash.

PS C:\Documents and Settings\Nick> echo '"hello"'
"hello"
PS C:\Documents and Settings\Nick> echo.exe '"hello"'
hello
PS C:\Documents and Settings\Nick> echo.exe '\"hello\"'
"hello"

Much easier than most of the workarounds here, but it would still be nice to get this bug fixed.

As long as we pass powershell a properly escaped string, it should automatically take care of whatever escaping is necessary for however it executes command line programs.
Posted by Softlion on 12/14/2010 at 10:22 PM
The first bug is:

$toto = "toto"
$test = ($toto,'-value='+$toto)
$test

Result by Powershell v2:
toto
-value=
toto

The correct result should be:

toto
-value=toto


The second bug is:

# Needs poweshell community extentions installed
Import-Module Pscx
$toto = "toto"
$test = ($toto,"-value=`"$toto`"")
Start-Process EchoArgs.exe -Wait -NoNewWindow -ArgumentList $test


Result by Powershell v2:

Arg 0 is <toto>
Arg 1 is <-value=toto>

The correct result should be:

Arg 0 is <toto>
Arg 1 is <-value="toto">
Posted by Oisín Grehan on 8/17/2010 at 7:51 AM
MSDeploy.exe (web deployment tool) is practically unusable in powershell because of the hellish nature of this problem.
Posted by Microsoft on 1/29/2010 at 1:55 PM
Your bug has not been fixed in PowerShell 2.0. However we have kept the bug active for next version
Posted by Joel 'Jaykul' Bennett on 11/4/2009 at 4:35 PM
Check the workarounds, that "Invoke-Legacy" is called Start-Process ;-)
Posted by Charlie Russel - MVP on 11/4/2009 at 3:45 PM
Sorry, fat fingers gave me double quotes in that comment when it should have been single quotes. But still lame. ;)
Posted by Charlie Russel - MVP on 11/4/2009 at 3:41 PM
Interesting suggestion from Kirk. I like it. Especially the alias. ;)

My current workaround is:
cmd /c "whatever it is I need to invoke"

Which is really stupid and lame, but PoSH doesn't always make it easy to avoid this. If our goal is to get rid of the need for CMD, then we need a way to more transparently handle this sort of scenario.
Posted by Kirk Munro on 11/4/2009 at 2:01 PM
Maybe this could be solved in PowerShell v3 with a new cmdlet, Invoke-LegacyCommand, that takes a script block with whatever legacy commands you want to invoke. Then it could simply parse that script block with specific rules for legacy commands (maybe evaluating variables inside double-quotes but leaving the rest as is), invoking the legacy commands correctly without a lot of quoting horseplay.

And you could give it a default alias of "cmd".... :)
Sign in to post a workaround.
Posted by Unregistered User on 9/12/2010 at 12:21 PM
To execute in PS we need scape $ not use second way
sqlcmd -v user=`"Joel Bennett`" -Q "select '`$(user)' as UserName"

Or
PS C:\test> $CmdLine="sqlcmd -v user=`"Joel Bennett`" -Q `"select '`$(user)' as UserName`""
PS C:\test> $CmdLine|cmd

Posted by Martin Zugec on 4/7/2010 at 7:58 AM
Instead of playing with ", ', '', ''''', """"" I personally prefer to use [Char]34, respectively $([Char]34).

Even though it sometimes takes longer to type (I use constant $QUOTE), you will get what you expect.
Posted by Tom Holt on 12/21/2009 at 2:36 PM
I gave up on any cmdlet provided by MS. Instead I use the Start method in the framework as follows:

[System.Diagnostics.Process]::Start("Cmd", "/c anything you want to put here with embedded quotes, and variables")

Works every time that I have used it.
Posted by Keith Hill MVP on 11/4/2009 at 7:28 PM
Start-Process doesn't work in this case e.g.:

PS> Microsoft.PowerShell.Management\start-process echoargs -Wait -NoNewWindow -Arg @'
>> -v -source:conn="Data Source=pubs" user="Joel Bennett" -Q "select '$(user)' as UserName"
>> '@
>>
Arg 0 is <-v>
Arg 1 is <-source:conn=Data Source=pubs>
Arg 2 is <user=Joel Bennett>
Arg 3 is <-Q>
Arg 4 is <select '$(user)' as UserName>

Note that arg 1 is missing the double quotes around "Data Source=pubs" and "Joel Bennett".
Posted by Joel 'Jaykul' Bennett on 11/4/2009 at 4:35 PM
In PowerShell 2, you can Use Start-Process:

Microsoft.PowerShell.Management\Start-Process sqlcmd -Arg @'
-v user="Joel Bennett" -Q "select '$(user)' as UserName"
'@ -Wait -NoNewWindow

It works perfectly, afaict (try it with the PSCX EchoArgs.exe).