Powershell:使用转义参数(--%)的Call运算符(&)无法处理非静态参数

7

我的PowerShell脚本需要调用一个带有非常复杂参数的EXE文件。我使用的是PowerShell 3.0版本,必须坚持使用该版本。遗憾的是,即使使用“魔法”转义运算符(--%),也无法帮助我。例如,使用Call运算符,可以考虑以下内容:

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

现在,如果这很简单的话,我的脚本就可以很容易地正常工作了。但事实并不那么简单。"other.exe"的参数可能会因用户在我的脚本中早期的选择而有所不同。因此,我需要提前构建这些参数,可能像这样:

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

因此,我会这样调用:
& other.exe --% $commandArgs

嗯,除非加上--%,否则它只是传递了一个原始的字符串$commandArgs。但是没有--%,PowerShell会自动引用$commandArgs的内容,这会使内部引号出现问题(更不用说破坏其他.exe需要的“action”参数了)。换句话说,我已经尝试将--%嵌入到我的$commandArgs字符串中,但是当解析时已经造成了损害(而且我认为它甚至不起作用)。

请注意,这个例子只是我需要执行的命令的四分之一左右,其中包括更多的用户参数、引号和其他有趣的字符,这些都会让我很快陷入转义地狱!我已经在使用echoargs.exe工具,这就是我遇到麻烦的原因。哦,我也需要我的例子中的所有空格(即需要大括号字符周围的空格)。

因此,在寻找答案的过程中,我求助于你的帮助。提前感谢你。

3个回答

10

好的,可能有点奇怪自问自答,但昨天在解决这个问题上又花了一整天,我也许已经找到了答案。至少,我发现这样行得通。但我在这里发帖是为了获得进一步的反馈,以防我真的做了不推荐的事情......使用 Invoke-Expression :-)

我早就意识到,而你们中的一些人在回复中也证实了这一点,--% 防止了所有后续的扩展(包括我的 $ 变量)。我的问题是,当尝试使用调用运算符 (&) 时,我仍需要扩展很多东西。如果在使用 --% 之前我的命令行已准备就绪,我的问题将得到解决,所以我就这样做了。

我创建了一个由以下组成的新字符串:

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

(实际的EXE路径中包含空格,因此需要加引号。)然后,将所有内容(包括--%在必要时)构建起来,将其作为新的脚本调用:

Invoke-Expression $fullCommand

到目前为止,我的结果非常好。但是在我迄今的搜索中,Invoke-Expression听起来像是人们不应该使用的坏东西。大家有什么想法?


3
< p > --% 的目的是抑制参数处理,因此在 PowerShell 中该参数后不会扩展变量。然而,您可以通过使用环境变量来解决这个问题:

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

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

这绝对是可行的解决方案,虽然需要大量变量“复制”(到环境变量中),随着我的命令行越来越长,可能会变得有点笨重...?我现在为此投上一票... :-)(或者,我想投票,但我还没有足够的声望,哦好吧) - SteveDJ

2

我建议人们将命令行参数构建成变量,然后将该变量传递到Start-Process cmdlet的-ArgumentList参数中。

$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;

嗯,这比我想象的要复杂一些。 :-) - SteveDJ
一旦你真正理解并习惯了它,实际上它会变得更容易。我还融入了.NET字符串格式化的概念,这不是必需的,但可以使参数编译更加模块化。 - user189198
这是否按预期工作?-ArgumentList参数应该是一个字符串数组,但你只给了它一个单独的字符串,所以$ArgumentList中的整个字符串应该作为第一个参数传递。 - poizan42
@poizan42 是的,它完美地运行。你也可以试试看。 - user189198

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接