powershell - 是否可以在脚本范围内执行包含脚本的字符串?
问题描述
记住XY 问题,这是我的 X 和 Y 问题。
X 问题
有 PowerShell 脚本,我已经混淆了其中的内容。但是,我仍然能够运行该脚本。
它的工作原理是脚本存储在可执行文件中。PowerShell 可以执行可执行文件,通过将脚本名称作为 CLI 参数传递来请求特定脚本。然后,可执行文件的进程将通过标准输出转发 PowerShell 脚本的内容。PowerShell 将标准输出存储在一个字符串变量中,然后调用Invoke-Expression,传入字符串,运行经过混淆的脚本。
问题是$PSScriptRoot,当它出现在混淆脚本中时,被评估为$null
.
问题
当 PowerShellscript.ps1
脚本以字符串形式执行 PowerShell 脚本时$scriptString
,如何$PSScriptRoot
像在script.ps1
.
测试 1
# script.ps1
$PSScriptRoot
# Output: Path to directory containing `script.ps1`.
这表明在至少一种情况下$PSScriptRoot
是非的。$null
测试 2
# script.ps1
Invoke-Expression '$PSScriptRoot'
# Output: Nothing
表明我当前执行字符串的方式不起作用。
测试 3
# script.ps1
. { $PSScriptRoot }
# Output: Path to directory containing `script.ps1`.
{ ... }
使用语法创建脚本块确实有效。然而,这需要实际的命令。我所拥有的是一个包含命令的字符串。将该字符串放在大括号之间会导致命令被输出而不是运行。
测试 4
# script.ps1
. [ScriptBlock]::Create('$PSScriptRoot')
# Output: Nothing
创建脚本块的另一种方法是使用 C#[ScriptBlock]::Create(...)
方法。这实际上允许从字符串创建脚本块。不幸的是,它不起作用。
测试 5
# script.ps1
Invoke-Command -ScriptBlock { $PSScriptRoot }
# Output: Path to directory containing `script.ps1`.
与测试 3 相同,除了调用Invoke-Command
脚本块而不是点源脚本块。
测试 6
# script.ps1
$scriptBlock = [ScriptBlock]::Create('$PSScriptRoot')
Invoke-Command -ScriptBlock $scriptBlock
# Output: Nothing.
与测试 3 相同,除了调用Invoke-Command
脚本块而不是点源脚本块。
目前对该问题的看法。
- 我所知道的将字符串脚本直接作为字符串执行的唯一方法是
Invoke-Expression
cmdlet,它不起作用。也许还有另一种直接执行字符串的方法可能有效,但我不知道。 - 执行字符串脚本的另一种方法是将字符串转换为脚本块,然后执行脚本块。似乎有两种创建脚本块的方法,一种有效,一种无效。
- 使用语法创建脚本块
{ ... }
似乎可行,但它似乎不适用于字符串。 - 使用该方法创建脚本块
[ScriptBlock]::Create(...)
不起作用,但可以使用字符串。
- 使用语法创建脚本块
问题:
- 有没有办法直接执行一个不需要调用就可以工作的字符串
Invoke-Expression
? - 有没有办法使用
{ ... }
语法从字符串创建脚本块? - 测试场景工作/不工作背后的原因/机制是什么?
- 最重要的是,我们如何执行字符串脚本并得到
$PSScriptRoot
正确的非$null
值?
解决方案
您可以使 PowerShell 将存储在字符串中的脚本视为已从磁盘读取- 也就是说,$PSScriptRoot
并且$PSCommandPath
将正确解析 - 通过手动解析它:
# Prepare script + path
$script = 'Write-Host "Script root: $PSScriptRoot`nCommand path: $PSCommandPath"'
$fileName = "C:\my\path\to\script.ps1"
# Make the parser generate an AST from the input script + path
$AST = [System.Management.Automation.Language.Parser]::ParseInput($script, $fileName, [ref]$null, [ref]$null)
# Get an executable scriptblock from AST
$block = $AST.GetScriptBlock()
# Now invoke it
& $block
此时您会发现$PS*
变量已正确解析:
Script root: C:\my\path\to
Command path: C:\my\path\to\script.ps1
推荐阅读
- html - 如何计算自定义 ValidityState
- c++ - How to group decimal places as well?
- hive - 为什么在 Hive 中映射器的数量变化如此之大以进行插入覆盖
- java - 无法解压缩 zip4j 库 java 创建的 zip 文件
- r - 如何修复R中的“替换有x行,数据有z”
- javafx - 为什么我的 CellFactory 中的第一个 TableRow 为空?
- php - 我从数据库中下载图像的功能不起作用
- html - 悬停时触发可翻转卡片的问题
- selenium - Chrome 无头不适用于重定向 (OAUTH2)
- reactjs - 如何在 React 中使用 web3?