繁体   English   中英

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

[英]Can't pass a script block as a parameter to powershell.exe via -Command

我正在尝试

$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"') 

没有帮助

而且我不确定我是否正确地在代码块中转义了变量,请阅读本章 ,但不确定如何将其应用于我的脚本。

我认为有些事情使它无法正常工作。 首先,当您使用单引号时, '是在指示PowerShell实际运行。 这意味着它不会扩展变量。 不是您要找的。

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

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

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

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

子表达式非常好用。 它使您可以在字符串中嵌入一个迷你脚本块,然后将其扩展到父字符串中。

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

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

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

  • 你想传递给新的任何参数powershell通过实例-Command必须以非明显的方式进行转义,如果它包含"和/或\\字符 ,这是特别容易,如果你经过一段PowerShell的源代码

    • 转义问题通常可以通过对源代码字符串进行Base64编码并通过-EncodedCommand参数传递来解决-有关如何执行此操作的信息,请参见我的回答 ,但下面提供了一种更为简洁的替代方法。
  • 如果传递的源代码引用了仅在调用会话中存在的任何变量,则新实例将看不到它们

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

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

$scriptBlock={
  param($projectFolder, $argList)
  # For demonstration, simply *output* the parameter values.
  "folder: [$projectFolder]; arguments: [$argList]"
}

现在,我们可以应用必要的转义,使用PetSerAl的复杂-replace从他对这个问题意见的表达。
然后,我们可以在传递参数值的同时,用& {...}调用结果字符串(为简便起见,我省略了-WorkingDirectory-PassThru参数):

# 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]

使用临时的自删除脚本文件的替代方法:

-File与脚本文件一起使用的优点是,可以将参数值作为文字传递,而无需担心对其内容的其他解释。

警告 :从PowerShell Core v6-beta.3开始,传递以-开头的参数值时出现问题:它们未按预期绑定; 请参阅此GitHub问题
若要解决此问题,下面的示例脚本块仅按名称访问第一个参数,并依赖于通过自动$Args变量绑定的所有其余$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

同样,以上结果:

folder: [c:\temp]; arguments: [-v -f]

我搞砸了将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'"

这项工作会:

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

& $Global:commandBlock

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM