无法通过-Command将脚本块作为参数传递给powershell.exe

时间:2017-02-14 16:37:39

标签: powershell escaping parameter-passing quoting

我正在尝试这个

$Global:commandBlock={
Start-Transcript -path $projectFolder\gruntLog.txt;
grunt $argList;
Stop-Transcript
}

$cmdProc=start-process powershell -ArgumentList ('-command `$Global:commandBlock') -WorkingDirectory $fwd -PassThru -NoNewWindow:$NoNewWindow

继续$commandBlock : The term '$Global:commandBlock' is not recognized as the name of a cmdlet, function, script file, or operable program.

我的猜测是它与范围有关。但改变全局并没有帮助。像这样添加-args $commandBlock

-ArgumentList ('-command `$Global:commandBlock -args "-commandBlock:$commandBlock"') 
-ArgumentList ('-command `$Global:commandBlock -args $commandBlock"') 

没有帮助

我不确定我是否在块中正确转义变量,请阅读this,但不知道如何应用于我的脚本。

4 个答案:

答案 0 :(得分:3)

我认为有一些事情可以防止这种情况发生。首先,当您使用单引号'时,您需要指示PowerShell按字面运算。这意味着它不会扩展变量。不是你想要的。

更好的方法是使用这样的子表达式。

$Global:commandBlock={
'ham' >> C:\temp\test.txt
}

$cmdProc=start-process powershell -ArgumentList ("-command $($Global:commandBlock)") -PassThru -NoNewWindow:$NoNewWindow

这将为您提供所需的结果。

Subexpressions非常甜蜜。它允许您在字符串中嵌入迷你脚本块,然后在父字符串中展开。

"today's date is $(get-date), on system: $($env:COMPUTERNAME)"
  

今天的日期是02/14/2017 11:50:49,关于系统:BEHEMOTH

答案 1 :(得分:1)

有两个主要问题(试图在单引号字符串中引用变量的明显错误除外):

  • 要通过powershell传递给新-Command个实例的任何参数,如果包含"和/或{,则必须以非显而易见的方式进行转义{1}}字符,如果您传递一段PowerShell 源代码,则特别有可能。

    • 转义问题通常可以通过对源代码字符串进行Base64编码并通过\参数传递来解决 - 请参阅我的this answer以了解如何执行该操作的相关问题,但下面将介绍一种更简洁的替代方案。
  • 如果传递的源代码引用了仅存在于调用会话中的任何变量,则新实例不会看到它们

    • 解决方案不是在传递的源代码中引用特定于会话的变量,而是将作为参数值传递。

要解决本地变量不能看到的新实例问题,我们必须重写脚本块以接受参数

-EncodedCommand

现在,我们可以使用PetSerAl的复杂$scriptBlock={ param($projectFolder, $argList) # For demonstration, simply *output* the parameter values. "folder: [$projectFolder]; arguments: [$argList]" } 表达式对问题进行必要的转义。 然后我们可以使用-replace调用生成的字符串,同时传递参数值(为了简洁,我省略了& {...}-WorkingDirectory参数):

-PassThru

有关正则表达式的解释,请再次参阅this answer

请注意作为参数传递给脚本块的变量值如何包含在# Parameter values to pass. $projectFolder = 'c:\temp' $argList='-v -f' Start-Process -NoNewWindow powershell -ArgumentList '-noprofile', '-command', (('& {' + $scriptBlock.ToString() + '}') -replace '\"|\\(?=\\*("|$))', '\$&'), "'$projectFolder'", "'$argList'" 内的'...'内的字符串中,以便:

  • 将值作为单个参数值传递。
  • 保护他们免受PowerShell的另一轮解释。

注意:如果您的变量值嵌入了"..."个实例,那么您必须将它们转义为'

以上产量:

''

替代临时自删除脚本文件:

folder: [c:\temp]; arguments: [-v -f] 与脚本文件结合使用,可以将参数值作为文字传递,而无需担心其内容的其他解释。

警告:从PowerShell Core v6-beta.3开始,传递以-File开头的参数值时出现问题:它们没有按预期绑定;见this GitHub issue
要解决此问题,下面的示例脚本块仅按名称访问第一个参数,并依赖于通过自动-变量绑定的所有剩余参数。

$Args

再次,上述收益率:

# Define the script block to be executed by the new PowerShell instance.
$scriptBlock={
  param($projectFolder)
  # For demonstration, simply *output* the parameter values.
  "folder: [$projectFolder]; arguments: [$Args]"
}

# Parameter values to pass.
$projectFolder = 'c:\temp'
$argList='-v -f'

# Determine the temporary script path.
$tempScript = "$env:TEMP\temp-$PID.ps1"

# Create the script from the script block and append the self-removal command.
# Note that simply referencing the script-block variable inside `"..."`
# expands to the script block's *literal* content (excluding the enclosing {...})
"$scriptBlock; Remove-Item `$PSCommandPath" > $tempScript

# Now invoke the temporary script file, passing the arguments as literals.
Start-Process -NoNewWindow powershell -ArgumentList '-NoProfile', '-File', $tempScript,
  $projectFolder,
  $argList

答案 2 :(得分:1)

我已经搞乱了将args传递给新的PowerShell实例的语法,并找到了以下工作。如此多的变化失败,没有良好的错误信息。也许它适合你的情况?

$arg = "HAM"
$command = {param($ham) write-host $ham}
 #please not its important to wrap your command 
 #in a further script block to stop it being processed to a string at execution
 #The following would normally suffice "& $command $arg"

Start-Process powershell -ArgumentList "-noexit -command & {$command}  $arg"

同样简单地使用Invoke-Command为您提供-ArgumentList参数,以便对标准powershell.exe参数缺少的给定命令进行操作。这可能看起来更清洁了。

Start-Process powershell -ArgumentList "-noexit -command invoke-command -scriptblock {$command} -argumentlist $arg"

无需任何额外的复杂转义或不需要的持久变量。只需将脚本块保留在花括号中,这样它在到达新会话时仍然是一个脚本块。至少在这个简单的情况下...

如果您有多个包含空格的字符串参数。我发现在单个括号中弹出字符串,用逗号分隔效果很好。您也可以将预定义数组作为单个参数传递。

Start-Process powershell -ArgumentList "-noexit -command invoke-command -scriptblock {$command} -argumentlist '$arg1', '$arg2', '$arg3'"

答案 3 :(得分:0)

这会起作用吗?

$Global:commandBlock={
Start-Transcript -path $projectFolder\gruntLog.txt;
grunt $argList;
Stop-Transcript
}

& $Global:commandBlock
相关问题