首页 > 解决方案 > 是否可以在脚本范围内执行包含脚本的字符串?

问题描述

记住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脚本块而不是点源脚本块。

目前对该问题的看法。

问题:

标签: powershell

解决方案


您可以使 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

推荐阅读