无法从函数内部修改脚本范围的变量

sim*_*pen 1 powershell scope powershell-2.0 powershell-remoting powershell-3.0

我目前正在制作一个脚本,该脚本应该连接到 42 个不同的本地服务器,并从活动目录获取特定组的用户(fj\xc3\xa4rrskrivbordsanv\xc3\xa4ndare(瑞典语远程桌面用户:D))。从服务器获取所有用户后,它必须将用户导出到我的桌面上的文件中

\n\n

csv 文件必须如下所示:

\n\n
Company;Users\nLawyerSweden;Mike\nLawyerSweden;Jennifer\nStockholm Candymakers;Pedro\n(Examples) \netc.\n
Run Code Online (Sandbox Code Playgroud)\n\n

这是目前的代码:

\n\n
cls\n\n$MolnGroup = \'fj\xc3\xa4rrskrivbordsanv\xc3\xa4ndare\' \n$ActiveDirectory = \'activedirectory\' \n$script:CloudArray\nSet-Variable -Name OutputAnvandare -Value ($null) -Scope Script\nSet-Variable -Name OutputDomain -Value ($null) -Scope Script\n\nfunction ReadInfo {\n\nWrite-Host("A")\n\nGet-Variable -Exclude PWD,*Preference | Remove-Variable -EA 0\n\nif (Test-Path "C:\\file\\frickin\\path.txt") {\n\n    Write-Host("File found")\n\n}else {\n\n    Write-Host("Error: File not found, filepath might be invalid.")\n    Exit\n}\n\n\n$filename = "C:\\File\\Freakin\'\\path\\super.txt"\n$Headers = "IPAddress", "Username", "Password", "Cloud"\n\n$Importedcsv = Import-csv $filename -Delimiter ";" -Header $Headers\n\n$PasswordsArray += @($Importedcsv.password)\n$AddressArray = @($Importedcsv | ForEach-Object { $_.IPAddress } )\n$UsernamesArray += @($Importedcsv.username)\n$CloudArray += @($Importedcsv.cloud)\n\nGetData\n} \n\nfunction GetData([int]$p) {\n\nWrite-Host("B") \n\nfor ($row = 1; $row -le $UsernamesArray.Length; $row++) \n{\n    # (If the customer has cloud-service on server, proceed) \n    if($CloudArray[$row] -eq 1)\n    {\n\n        # Code below uses the information read in from a file to connect pc to server(s)\n\n        $secstr = New-Object -TypeName System.Security.SecureString \n        $PasswordsArray[$row].ToCharArray() | ForEach-Object {$secstr.AppendChar($_)}\n        $cred = new-object -typename System.Management.Automation.PSCredential -argumentlist $UsernamesArray[$row], $secstr\n\n        # Runs command on server\n\n        $OutputAnvandare = Invoke-Command -computername $AddressArray[$row] -credential $cred -ScriptBlock {\n\n            Import-Module Activedirectory                \n\n            foreach ($Anvandare in (Get-ADGroupMember fj\xc3\xa4rrskrivbordsanv\xc3\xa4ndare)) \n            {\n\n                $Anvandare.Name\n            }\n        }\n\n        $OutputDomain = Invoke-Command -computername $AddressArray[$row] -credential $cred -ScriptBlock {\n\n        Import-Module Activedirectory                \n\n            foreach ($Anvandare in (Get-ADGroupMember fj\xc3\xa4rrskrivbordsanv\xc3\xa4ndare)) \n            {\n\n                gc env:UserDomain\n            }\n        }\n\n    $OutputDomain + $OutputAnvandare\n    }\n  }\n}\n\n\nfunction Export {\n\nWrite-Host("C")\n\n# Variabler f\xc3\xb6r att bygga up en CSV-fil genom Out-File\n$fils\xc3\xb6kv\xc3\xa4g = "C:\\my\\file\\path\\Coolkids.csv"\n$ColForetag = "Company"\n$ColAnvandare = "Users"\n$Emptyline = "`n"\n$delimiter = ";"\n\nfor ($p = 1; $p -le $AA.Length; $p++) {\n\n    # writes out columns in the csv file\n    $ColForetag + $delimiter + $ColAnvandare | Out-File $fils\xc3\xb6kv\xc3\xa4g\n\n    # Writes out the domain name and the users\n    $OutputDomain + $delimiter + $OutputAnvandare | Out-File $fils\xc3\xb6kv\xc3\xa4g -Append\n\n    }\n}\n\nReadInfo\nExport\n
Run Code Online (Sandbox Code Playgroud)\n\n

我的问题是,我无法导出用户或域。正如您所看到的,我尝试将变量设置为整个脚本的全局变量,但 $outputanvandare 和 $outputdomain 仅包含我在 foreach 循环内需要的信息。如果我尝试在其他地方打印它们,它们是空的?!

\n

mkl*_*nt0 5

这个答案重点关注变量范围,因为它是问题的直接原因。
然而,值得一提的是,最好从一开始就避免跨范围修改变量;相反,通过成功流传递值(或者不太常见的是通过引用变量和参数 ( [ref]) 传递值。

详细阐述PetSerAl对这个问题的有用评论:有关 PowerShell 变量范围的可能违反直觉的事情是:

  • 虽然您可以通过仅通过名称(例如, )引用它们来查看(读取)祖先(更高)作用域(例如父作用域)中的变量$OutputDomain

  • 您不能仅通过名称修改它们- 要修改它们,您必须显式引用它们定义的范围。

如果没有范围限定,则分配给祖先范围中定义的变量会隐式在当前范围中创建一个同名的变量。

演示该问题的示例:

  # Create empty script-level var.
  Set-Variable -Scope Script -Name OutputDomain -Value 'original'
  # This is the same as:
  #   $script:OutputDomain = 'original'

  # Declare a function that reads and modifies $OutputDomain
  function func {

    # $OutputDomain from the script scope can be READ
    # without scope qualification:        
    $OutputDomain  # -> 'original'

    # Try to modify $OutputDomain.
    # !! Because $OutputDomain is ASSIGNED TO WITHOUT SCOPE QUALIFICATION
    # !! a NEW variable in the scope of the FUNCTION is created, and that
    # !! new variable goes out of scope when the function returns.
    # !! The SCRIPT-LEVEL $OutputDomain is left UNTOUCHED.
    $OutputDomain = 'new'

    # !! Now that a local variable has been created, $OutputDomain refers to the LOCAL one.
    # !! Without scope qualification, you cannot see the script-level variable
    # !! anymore.
    $OutputDomain  # -> 'new'
  }

  # Invoke the function.
  func

  # Print the now current value of $OutputDomain at the script level:
  $OutputDomain # !! -> 'original', because the script-level variable was never modified.
Run Code Online (Sandbox Code Playgroud)

解决方案:

有多种方法可以将范围限定添加到变量引用

  • 使用范围修饰符,例如script$script:OutputDomain.

    • 对于当前的情况,这是最简单的解决方案:
      $script:OutputDomain = 'new'

    • 请注意,这仅适用于绝对范围globalscriptlocal(默认)。

    • 关于global变量的警告:它们是会话全局变量,因此分配给全局变量的脚本可能会无意中修改预先存在的全局变量,相反,在脚本内创建的全局变量在脚本终止后继续存在。

  • 使用Get/Set-Variable -Scope,除了支持绝对作用域修饰符之外,还支持通过基于 0 的索引进行相对作用域引用,其中表示当前作用域、父作用域等。01

    • 在当前的情况下,由于脚本作用域是下一个更高的作用域,因此与, 和equals
      Get-Variable -Scope 1 OutputDomain相同。$script:OutputDomain
      Set-Variable -Scope 1 OutputDomain 'new'$script:OutputDomain = 'new'
  • (函数和陷阱处理程序内部很少使用的替代方法是使用[ref],它允许在定义变量的最直接的祖先范围内修改变量: ([ref] $OutputDomain).Value = 'new',正如 PetSerAl 在注释中指出的那样,与 相同(Get-Variable OutputDomain).Value = 'new'

有关更多信息,请参阅:


最后,为了完整起见,Set-Variable -Option AllScope这是一种完全避免使用范围限定(在所有后代范围中)的方法,因为实际上只有一个具有该名称的变量存在,可以在没有范围限定的情况下从任何地方读取修改该变量。 (后代)范围。

  # By defining $OutputDomain this way, all descendent scopes
  # can both read and assign to $OutpuDomain without scope qualification
  # (because the variable is effectively a singleton).
  Set-Variable -Scope Script -Option AllScope -Name OutputDomain
Run Code Online (Sandbox Code Playgroud)

但是,我不会推荐它(至少在不采用命名约定的情况下),因为它模糊了修改局部变量和所有范围变量之间的区别:
在没有范围限定的情况下,单独查看诸如这样的语句$OutputDomain = 'new',您无法判断是否正在修改局部变量或全范围变量。