Powershell: Call operator (&) with escape param (-

2019-04-08 16:30发布

问题:

My Powershell script needs to invoke an EXE with a very complicated set of arguments. I'm using Powershell 3.0, and must stick with that version. Alas, even the "magic" escaping operator (--%) isn't helping me. For example, using the Call operator, consider this:

& other.exe --% action /mode fast /path:"location with spaces" /fancyparam { /dothis /dothat:"arg with spaces" } /verbose

Now, if it were that simple, my script could easily work fine. But it isn't that simple. The arguments for "other.exe" can be different, depending on user selections earlier in my script. So instead, I need to build up those parameters ahead of time, perhaps like this:

$commandArgs = 'action /mode ' + $userMode + ' /path:"location with spaces" /fancyparam { /dothis /dothat:"' + $userArgs + " } /verbose'

Thus I would invoke this way:

& other.exe --% $commandArgs

...well, expect that --% means it just passes a raw string of $commandArgs instead. But without the --%, powershell auto-quotes the contents of $commandArgs, which really messes up the internal quotes (not to mention breaking the 'action' argument at the front that other.exe needs first). In other words, I've already tried embedding the --% inside my $commandArgs string, but the damage is already done by the time it would be parsed (and I don't think it even works that way).

NOTE that this example is only about 1/4 of my actual command I need to execute -- which includes many more user args, quotes and other funny characters that would drive me into escaping-hell in a hurry! I've also already been using the echoargs.exe tool, which is how I'm seeing the troubles I'm having. Oh, and I need all the spaces in my example, too (i.e. need spaces around the brace characters).

So after much searching for an answer, I turn to you for help. Thanks in advance.

回答1:

Ok, probably weird to be answering my own question, but after spending another day on this problem yesterday, I might have realized the answer myself. At least, this is what I found that works. But I post here to get further feedback, in case I'm really doing something that isn't recommended ...using Invoke-Expression :-)

I had sorta realized early on, and some of you confirmed this in your responses, that the --% prevents all further expansions (including my $variables). And my problem is that I'm still needing to expand lots of things when trying to use the Call operator (&). My problem would be solved if my command line was all ready to go before using --%, so that's what I did.

I created a new string, composed of:

$fullCommand = '& "other.exe" --% ' + $commandArgs

(The EXE path actually has spaces in it, hence the quotes.) Then, with it all built up (including the --% where it needs to be), I invoke it as a new piece of script:

Invoke-Expression $fullCommand

So far, I'm having very good results. But I know in my search up to this point that Invoke-Expression sounded like this bad thing people shouldn't use. Thoughts, everyone?



回答2:

The purpose of --% is to suppress argument processing, so there's no expansion of variables after that parameter on behalf of PowerShell. You can work around this by using environment variables, though:

$env:UserMode = 'foo'
$env:UserArgs = 'bar baz'

& other.exe --% action /mode %UserMode% /path:"location with spaces" /fancyparam { /dothis /dothat:"%userArgs%" } /verbose


回答3:

I always recommend that people build their command line arguments into a variable, and then pass that variable into the -ArgumentList parameter of the Start-Process cmdlet.

$Program = '{0}\other.exe' -f $PSScriptRoot;
$UserMode = 'fast';
$Path = 'c:\location with\spaces';
$ArgWithSpaces = 'arg with spaces';
$ArgumentList = 'action /mode {0} /path:"{1}" /fancyparam { /dothis /dothat:"{2}" } /verbose' -f $UserMode, $Path, $ArgWithSpaces;

$Output = '{0}\{1}.log' -f $env:Temp, [System.Guid]::NewGuid().ToString();
Start-Process -Wait -FilePath $Program -ArgumentList $ArgumentList -RedirectStandardOutput $Output;