无法通过 -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"')
没有帮助
而且我不确定我是否在块中正确转义变量,阅读 this,但不确定如何应用到我的脚本。
这行得通吗:
$Global:commandBlock={
Start-Transcript -path $projectFolder\gruntLog.txt;
grunt $argList;
Stop-Transcript
}
& $Global:commandBlock
我认为有几件事阻碍了它的工作。首先,当您使用单引号 '
时,您是在指示 PowerShell 按字面意思进行操作。这意味着它不会扩展变量。不是你要找的。
更好的方法是使用这样的子表达式。
$Global:commandBlock={
'ham' >> C:\temp\test.txt
}
$cmdProc=start-process powershell -ArgumentList ("-command $($Global:commandBlock)") -PassThru -NoNewWindow:$NoNewWindow
这会给你想要的结果。
子表达式非常可爱。它允许您在字符串中嵌入 mini-scriptblock,然后它会在父字符串中展开。
"today's date is $(get-date), on system: $($env:COMPUTERNAME)"
today's date is 02/14/2017 11:50:49, on system: BEHEMOTH
有两个主要问题(撇开试图在 单引号 字符串中引用变量的明显错误):
你想通过 -Command
传递给新的 powershell
实例的任何参数如果包含 "
and/or \
chars,如果你传递的是一段 PowerShell 源代码.
,这种情况尤其可能发生
- 转义问题通常可以通过 Base64 编码源代码字符串并通过
-EncodedCommand
参数传递来解决 - 请参阅我的 this answer 的相关问题了解如何做到这一点, 但下面提供了一个更简洁的替代方案。
如果传递的源代码引用任何仅存在于 调用 会话中的变量,新实例将看不到它们.
- 解决方案不是在传递的源代码中引用特定于会话的变量,而是将它们的值作为参数值传递相反。
要解决局部变量未被新实例看到的问题,我们必须重写脚本块以接受参数:
$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'"
有关正则表达式的解释,请再次参阅this answer。
请注意如何将作为参数传递给脚本块的变量值包含在 "..."
内的 '...'
中,以便:
- 将值作为单个参数值传递。
- 保护它们免受 PowerShell 的另一 轮解释。
注意:如果您的变量值嵌入了 '
个实例,您必须将它们转义为 ''
.
以上结果:
folder: [c:\temp]; arguments: [-v -f]
使用临时的自删除脚本文件的替代方法:
将 -File
与脚本 file 一起使用的优点是能够将参数值作为 文字 传递,而无需关注对其内容的额外解释。
警告:从 PowerShell Core v6-beta.3 开始,传递以 -
开头的参数值时出现问题:它们未按预期绑定;参见 this GitHub issue。
要解决此问题,下面的示例脚本块仅按名称访问 first 参数,并依赖于通过自动 $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
}
$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,但不确定如何应用到我的脚本。
这行得通吗:
$Global:commandBlock={
Start-Transcript -path $projectFolder\gruntLog.txt;
grunt $argList;
Stop-Transcript
}
& $Global:commandBlock
我认为有几件事阻碍了它的工作。首先,当您使用单引号 '
时,您是在指示 PowerShell 按字面意思进行操作。这意味着它不会扩展变量。不是你要找的。
更好的方法是使用这样的子表达式。
$Global:commandBlock={
'ham' >> C:\temp\test.txt
}
$cmdProc=start-process powershell -ArgumentList ("-command $($Global:commandBlock)") -PassThru -NoNewWindow:$NoNewWindow
这会给你想要的结果。
子表达式非常可爱。它允许您在字符串中嵌入 mini-scriptblock,然后它会在父字符串中展开。
"today's date is $(get-date), on system: $($env:COMPUTERNAME)"
today's date is 02/14/2017 11:50:49, on system: BEHEMOTH
有两个主要问题(撇开试图在 单引号 字符串中引用变量的明显错误):
你想通过
,这种情况尤其可能发生-Command
传递给新的powershell
实例的任何参数如果包含"
and/or\
chars,如果你传递的是一段 PowerShell 源代码.- 转义问题通常可以通过 Base64 编码源代码字符串并通过
-EncodedCommand
参数传递来解决 - 请参阅我的 this answer 的相关问题了解如何做到这一点, 但下面提供了一个更简洁的替代方案。
- 转义问题通常可以通过 Base64 编码源代码字符串并通过
如果传递的源代码引用任何仅存在于 调用 会话中的变量,新实例将看不到它们.
- 解决方案不是在传递的源代码中引用特定于会话的变量,而是将它们的值作为参数值传递相反。
要解决局部变量未被新实例看到的问题,我们必须重写脚本块以接受参数:
$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'"
有关正则表达式的解释,请再次参阅this answer。
请注意如何将作为参数传递给脚本块的变量值包含在 "..."
内的 '...'
中,以便:
- 将值作为单个参数值传递。
- 保护它们免受 PowerShell 的另一 轮解释。
注意:如果您的变量值嵌入了 '
个实例,您必须将它们转义为 ''
.
以上结果:
folder: [c:\temp]; arguments: [-v -f]
使用临时的自删除脚本文件的替代方法:
将 -File
与脚本 file 一起使用的优点是能够将参数值作为 文字 传递,而无需关注对其内容的额外解释。
警告:从 PowerShell Core v6-beta.3 开始,传递以 -
开头的参数值时出现问题:它们未按预期绑定;参见 this GitHub issue。
要解决此问题,下面的示例脚本块仅按名称访问 first 参数,并依赖于通过自动 $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'"