PowerShell脚本离线重启电脑时变量值为空问题求助
PowerShell脚本离线重启问题排查与修复建议
我编写了一个PowerShell脚本,目标是仅在电脑离线时重启设备。脚本中获取用户信息的功能正常,但底部重启逻辑部分的$Online和$Computername变量值为空($null)。使用PDQ Inventory部署该脚本,仅针对单用户场景,脚本示例如下:
Function Get-LoggedOnUser { Param ( $ComputerName = $env:COMPUTERNAME, $Credential ) Function Test-RemoteRegistry { Param ( [Parameter(Mandatory = $False)] [switch]$Enable , [Parameter(Mandatory = $False)] [switch]$Disable , [Parameter(ValueFromPipeline=$True)] [String[]]$ComputerName = $env:COMPUTERNAME ) Begin { $PipelineInput = (-not $PSBOUNDPARAMETERS.ContainsKey("ComputerName")) -and (-not $ComputerName) Function Test ($Computer) { Try { [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey([Microsoft.Win32.RegistryHive]::LocalMachine, $Computer) | Out-Null #20ms faster than Get-Service per computer! Not sure how to handle/check things like the firewall though... #If we hit here without error Remote Reg is enabled. If ($Disable) { Try { Get-Service -Name RemoteRegistry -ComputerName $Computer | Set-Service -Status Stopped -ErrorAction Stop Return $False #If we hit here without error Remote Reg is now disabled. } Catch { Return $True #If we hit here, we couldn't stop remote registry. } } Else { Return $True } } Catch { If ($Enable) { Try { Get-Service -Name RemoteRegistry -ComputerName $Computer | Set-Service -Status Running -ErrorAction Stop Return $True #If we hit here without error Remote Reg is now enabled. } Catch { Return $False #If we hit here, we couldn't start remote registry. } } Else { Return $False #If we hit here remote registry is disabled. } } } } Process { If ($PipelineInput) { Test $_ } Else { $ComputerName | ForEach-Object { Test $_ } } } } Foreach ($Computer in $Computername) { $Online = $False $User = $False $Locked = $False If (Test-Connection $Computer -Count 2 -Quiet) { $Online = $True If ($Credential) { $User = Get-WmiObject -Class Win32_ComputerSystem -ComputerName $Computer -Credential $Credential | Select-Object -ExpandProperty UserName -ErrorAction Stop } Else { $User = Get-WmiObject -Class Win32_ComputerSystem -ComputerName $Computer | Select-Object -ExpandProperty UserName -ErrorAction Stop } If (Test-RemoteRegistry -Enable -ComputerName $Computer) { If ((Get-Process logonui -ComputerName $Computer -ErrorAction SilentlyContinue) -and ($user)) { $Locked = $True } } } $Output = New-Object PSObject $Output | Add-Member noteproperty ComputerName $Computer $Output | Add-Member noteproperty Online $Online $Output | Add-Member noteproperty Username $User $Output | Add-Member noteproperty Locked $Locked $Output } } Get-LoggedOnUser If (($Online) -eq $False) {Shutdown /r t 0 /m \$Computername} ELSE {Write-host 'HELLO $Online $Computername'}
问题根源
核心问题是变量作用域限制:
$Online和$Computername是定义在Get-LoggedOnUser函数内部的局部变量,函数外部无法直接访问这些变量。- 函数外的
$Online属于全局作用域,未被赋值所以默认是$null;$Computername也未在全局作用域定义,自然也是$null。 - 原脚本中
Write-host 'HELLO $Online $Computername'使用单引号,单引号不会解析变量,即使变量有值也无法正确显示。
修复方案与修正脚本
方案1:捕获函数返回对象提取属性(推荐)
通过变量捕获Get-LoggedOnUser返回的自定义对象,从中获取ComputerName和Online属性进行判断:
Function Get-LoggedOnUser { Param ( $ComputerName = $env:COMPUTERNAME, $Credential ) Function Test-RemoteRegistry { Param ( [Parameter(Mandatory = $False)] [switch]$Enable , [Parameter(Mandatory = $False)] [switch]$Disable , [Parameter(ValueFromPipeline=$True)] [String[]]$ComputerName = $env:COMPUTERNAME ) Begin { $PipelineInput = (-not $PSBOUNDPARAMETERS.ContainsKey("ComputerName")) -and (-not $ComputerName) Function Test ($Computer) { Try { [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey([Microsoft.Win32.RegistryHive]::LocalMachine, $Computer) | Out-Null If ($Disable) { Try { Get-Service -Name RemoteRegistry -ComputerName $Computer | Set-Service -Status Stopped -ErrorAction Stop Return $False } Catch { Return $True } } Else { Return $True } } Catch { If ($Enable) { Try { Get-Service -Name RemoteRegistry -ComputerName $Computer | Set-Service -Status Running -ErrorAction Stop Return $True } Catch { Return $False } } Else { Return $False } } } } Process { If ($PipelineInput) { Test $_ } Else { $ComputerName | ForEach-Object { Test $_ } } } } Foreach ($Computer in $Computername) { $Online = $False $User = $False $Locked = $False If (Test-Connection $Computer -Count 2 -Quiet) { $Online = $True If ($Credential) { $User = Get-WmiObject -Class Win32_ComputerSystem -ComputerName $Computer -Credential $Credential | Select-Object -ExpandProperty UserName -ErrorAction Stop } Else { $User = Get-WmiObject -Class Win32_ComputerSystem -ComputerName $Computer | Select-Object -ExpandProperty UserName -ErrorAction Stop } If (Test-RemoteRegistry -Enable -ComputerName $Computer) { If ((Get-Process logonui -ComputerName $Computer -ErrorAction SilentlyContinue) -and ($user)) { $Locked = $True } } } $Output = New-Object PSObject $Output | Add-Member noteproperty ComputerName $Computer $Output | Add-Member noteproperty Online $Online $Output | Add-Member noteproperty Username $User $Output | Add-Member noteproperty Locked $Locked $Output } } # 捕获函数返回的状态对象 $computerStatus = Get-LoggedOnUser # 从对象中提取属性执行逻辑 If (-not $computerStatus.Online) { # 远程关机命令需要双反斜杠 Shutdown /r /t 0 /m \\$($computerStatus.ComputerName) } Else { # 双引号解析变量,正确显示状态 Write-Host "设备 $($computerStatus.ComputerName) 处于在线状态,在线标记:$($computerStatus.Online)" }
方案2:单用户场景简化脚本
如果仅需要判断离线状态并重启,可简化逻辑,直接在函数内完成所有操作,避免变量作用域问题:
Function Restart-OfflineComputer { Param ( $ComputerName = $env:COMPUTERNAME, $Credential ) # 先判断设备是否在线 $online = Test-Connection $ComputerName -Count 2 -Quiet if (-not $online) { Write-Host "设备 $ComputerName 离线,执行重启..." Shutdown /r /t 0 /m \\$ComputerName } else { # 在线时保留原逻辑获取用户信息 try { $user = if ($Credential) { Get-WmiObject -Class Win32_ComputerSystem -ComputerName $ComputerName -Credential $Credential | Select-Object -ExpandProperty UserName -ErrorAction Stop } else { Get-WmiObject -Class Win32_ComputerSystem -ComputerName $ComputerName | Select-Object -ExpandProperty UserName -ErrorAction Stop } Write-Host "设备 $ComputerName 在线,当前登录用户:$user" } catch { Write-Host "设备 $ComputerName 在线,但无法获取登录用户信息:$_" } } } # 执行函数 Restart-OfflineComputer
内容的提问来源于stack exchange,提问作者KEGS
相关产品推荐
相关产品推荐

