管理与非管理模式-由于变量已优化,因此无法覆盖变量

时间:2018-10-09 20:54:46

标签: windows powershell batch-file variables visual-studio-code

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

如果有帮助的话,可以在Windows 10 Build 1709上进行测试

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

导致错误的方式

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

避免错误的方法

  • 管理员模式打开PowerShell ISE,然后打开/运行脚本将不会引起错误
  • 在最后两行代码的变量前添加Script:,无论脚本如何执行,都不会不会导致错误
  • 使用VSCode,它将如下所示工作。在集成终端中运行它,它将看到它不是以Admin的身份运行,它将在VSCode之外启动PowerShell.exe并正常工作

-

为什么在函数中的变量前面有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'"

1 个答案:

答案 0 :(得分:1)

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

您的问题听起来与DLR (Dynamic Language Runtime) 相关的PowerShell bug ,PowerShell在幕后使用的一项技术(自v3起);至少有一个bug report on GitHub听起来很相关。


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

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

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

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

如果要设置很多单个变量,则可以使函数输出为hash table,并使用哈希表的命名条目代替单个变量(由于使用{{1,因此需要PSv3 + }},以创建具有顺序键的哈希表):

[ordered]