4

在今天的一些测试中,我遇到了一个意想不到的问题,我不明白为什么会这样。下面是我用来复制问题的代码。它只是较大项目的一小部分。

如果有帮助,正在 Windows 10 Build 1709 上进行测试

PS1 文件和 BAT 文件的名称相同。

导致错误的方法

  • 通过运行 PS1 文件Right-Click - Run with PowerShell会导致错误
  • PowerShell ISE非管理员模式打开,然后打开/运行脚本将导致错误
  • 以管理员或非管理员身份运行 BAT 文件将导致错误

避免错误的方法

  • PowerShell ISE管理员模式打开,然后打开/运行脚本不会导致错误
  • Script:在最后2行代码的变量前面加上不管脚本怎么执行都不会报错
  • 使用 VSCode,它将如下所示工作。在集成终端中运行它,它会看到它没有作为 运行Admin,它将PowerShell.exe在 VSCode 之外启动并且没有问题地工作

-

为什么我Script:的函数前面有变量?这是我可以在函数中设置变量以在函数外使用的唯一方法。这篇文章中未列出的其他 25 个左右的变量没有问题,但是,它们在设置后不会像这两个一样被修改。

问题

  • 为什么,如果在Admin模式下运行 ISE,它会工作?
  • 如果它以管理员身份重新启动,为什么它不起作用?
  • 为什么 VSCode 不关心并且无论如何它都可以工作?

有些东西没有意义,我无法确定它。

这是错误

无法覆盖变量 NetFX3,因为该变量已被优化。尝试使用 New-Variable 或 Set-Variable cmdlet(不带任何别名),或点源您用于设置变量的命令。在 C:\Users\a502690530\Desktop\Testing2.ps1:14 char:5 + [string]$Script:NetFX3 = $BAT_Files_Path + "NetFX3.zip" + ~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : WriteError: (NetFX3:String) [], SessionStateUnauthorizedAccessException + FullyQualifiedErrorId : VariableNotWritableRare

无法覆盖变量 Power_Plan,因为该变量已被优化。尝试使用 New-Variable 或 Set-Variable cmdlet(不带任何别名),或点源您用于设置变量的命令。在 C:\Users\a502690530\Desktop\Testing2.ps1:15 char:5 + [string]$Script:Power_Plan = $BAT_Files_Path + "Power_Plan.zip" + ~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ CategoryInfo : WriteError: (Power_Plan:String) [], SessionStateUnauthorizedAccessException + FullyQualifiedErrorId : VariableNotWritableRare

这是代码

# Checks if running as an administrator. If not, it will relaunch as an administrator
If (-Not ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) {
    $Arguments = "& '" + $MyInvocation.MyCommand.Definition + "'"
    Start-Process Powershell -Verb RunAs -ArgumentList $Arguments
    Break
}

[string]$ErrorActionPreference = "Continue"
[string]$BAT_Files = $Root_Path + "BAT_Files\"

Function Set-FilePaths ([string]$BAT_Files_Path) {
    # BAT Files Paths (ZIPs only!!!)
    [string]$Script:NetFX3     = $BAT_Files_Path + "NetFX3.zip"
    [string]$Script:Power_Plan = $BAT_Files_Path + "Power_Plan.zip"

    Set-Lists
}

function Set-Lists {
    # List of BAT Files (ZIPs)
    [System.Collections.ArrayList]$Script:List_Of_BAT_Files = @(
        $NetFX3
        $Power_Plan
    )
}

Set-FilePaths `
    -BAT_Files_Path $BAT_Files

PAUSE

$NetFX3 = ((Split-Path $NetFX3 -Parent) + "\NetFX3\")
$Power_Plan = ((Split-Path $Power_Plan -Parent) + "\Power_Plan\")

BAT 文件启动

REG ADD "HKLM\SOFTWARE\Microsoft\PowerShell\1\ShellIds\Microsoft.PowerShell" /T REG_SZ /V ExecutionPolicy /D Unrestricted /F

Start PowerShell.exe -Command "& '%~dpn0.ps1'"
4

1 回答 1

1

我没有具体的答案,但有一个指针:

您的问题听起来像是与DLR (Dynamic Language Runtime)相关的 PowerShell错误,这是 PowerShell 在幕后使用的一种技术(自 v3 起);GitHub上至少有一个公开的错误报告听起来相关。


除了您已经知道的解决方法 -script始终使用范围修饰符 -我建议避免跨范围边界访问变量作为一般最佳实践,这也应该避免这个问题。

PowerShell 在可以从函数返回(输出)的内容方面非常灵活,因此最好根据函数的输出在调用者的范围内设置变量。

具体来说,我建议重构您的代码如下:

Function Get-FilePaths ([string]$BAT_Files_Path) {
  # Output the paths as an *array*.
  ($BAT_Files_Path + "NetFX3.zip"), ($BAT_Files_Path + "Power_Plan.zip")
}

# Call the function in the script scope and capture its output in variables.
$List_Of_BAT_Files = Get-FilePaths

# Use a destructuring assignment to store the elements of the array 
# in individual variables
$NetFX3, $Power_Plan = $List_Of_BAT_Files

如果有很多单独的变量要设置,你可以让函数输出一个哈希表,并使用哈希表的命名条目而不是单独的变量(需要 PSv3+,由于使用[ordered]创建带有有序键的哈希表) :

Function Get-FilePaths ([string]$BAT_Files_Path) {
  # Output the paths as a *hash table*, using its
  # entries for named access instead of individual variables.
  $outHash = [ordered] @{
    NetFX3 = $BAT_Files_Path + "NetFX3.zip"
    Power_Plan = $BAT_Files_Path +  "Power_Plan.zip"
  }
  # Add a 'List' entry that contains all values added above as an array.
  # Note the need to use @(...) to force creation of a new array from the
  # hash table's value collection.
  $outHash.List = @($outHash.Values)
  # Output the hash table.
  $outHash
}

# Call the function in the script scope and capture its output in 
# a single variable that receives the hash table.
$hash = Get-FilePaths

# Now you can access the invididual values by name - e.g., $hash.NetFX3 -
# or use $hash.List to get all values.
于 2018-10-10T14:25:37.207 回答