在循环期间变量不重置,从而产生误导性输出

时间:2015-06-17 23:14:19

标签: powershell error-handling

我这里有一个脚本,可以读取桌面列表并更改这些桌面的管理员密码。它有效,但我有一个小问题。一旦错误出现在Powershell中,该值永远不会重置。因此,例如第二个DT失败,第三个和第四个DT成功。在错误列下,即使密码更改成功,它也会显示第二个DT的错误。但是,它在输出文件(outputs.txt)上工作正常。我试过$ error.clear(),但它没有解决我的问题。也许我把它放在错误的地方?如果你能帮助我,我会很感激,因为我完成了99.9%。谢谢。

[cmdletbinding()]
param (
[parameter(mandatory = $true)]
$InputFile,
$OutputDirectory
)

#Log failures in an output file called "outputs.txt" stored in the directory of input file
if(!$outputdirectory) {
    $outputdirectory = (Get-Item $InputFile).directoryname
}   
$failedcomputers    =   Join-Path $outputdirectory "outputs.txt"
$stream = [System.IO.StreamWriter] $failedcomputers
$stream.writeline("ComputerName `t IsOnline `t PasswordChangeStatus `t Error")
$stream.writeline("____________ `t ________ `t ____________________ `t _____")

#Prompt to confirm password twice and read password in a secure manner
$password = Read-Host "Enter the password" -AsSecureString
$confirmpassword = Read-Host "Confirm the password" -AsSecureString

#Convert password into plain text for comparison
$pwd1_text = [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($password))
$pwd2_text = [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($confirmpassword))

#If the passwords don't match, script exits; otherwise, it continues.
if($pwd1_text -ne $pwd2_text) {
    Write-Error "Entered passwords are not same. Script is exiting."
    exit
}

#Check if input file exists. If file not found, script exits.
if(!(Test-Path $InputFile)) {
    Write-Error "File ($InputFile) not found. Script is exiting."
    exit
}

#Read contents of input file using Get-Content cmdlet and store list in an array called $computers
$Computers = Get-Content -Path $InputFile

#Loop through each computer account in the array and check if online or not using Test-Connection cmdlet
foreach ($Computer in $Computers) {
    $Computer   =   $Computer.toupper()
    $Isonline   =   "OFFLINE"
    $Status     =   "SUCCESS"
    Write-Verbose "Working on $Computer"
    if((Test-Connection -ComputerName $Computer -count 1 -ErrorAction 0)) {
        $Isonline = "ONLINE"
        Write-Verbose "`t$Computer is Online"
    } else { Write-Verbose "`t$Computer is OFFLINE" }

    #If ping is successful, password is changed
    try {
        $account = [ADSI]("WinNT://$Computer/Administrator,user")
        $account.psbase.invoke("setpassword",$pwd1_text)
        Write-Verbose "`tPassword Change completed successfully"
    }

    #If password change fails, respective error is recorded
    catch {
        $status = "FAILED"
        Write-Verbose "`tFailed to Change the administrator password. Error: $_"
        $e = $_.Exception
        $errmsg = $e.Message
        while ($e.InnerException) {
            $e = $e.InnerException
            $errmsg = $e.Message
        }
    }

    $obj = New-Object -TypeName PSObject -Property @{
        ComputerName = $Computer
        IsOnline = $Isonline
        PasswordChangeStatus = $Status
        Error = $errmsg
    }

    $obj | Select ComputerName, IsOnline, PasswordChangeStatus, Error

    if($Status -eq "FAILED" -or $Isonline -eq "OFFLINE") {
        $stream.writeline("$Computer `t`t $isonline `t`t $status `t $errmsg")
    }else{
        $stream.writeline("$Computer `t`t $isonline `t`t $status")
    }

}
$stream.close()
Write-Host "`n`nFailed computers list is saved to $failedcomputers"

2 个答案:

答案 0 :(得分:4)

从我的小图片中可以看出,这个输出线正在引起你的悲痛:

$obj | Select ComputerName, IsOnline, PasswordChangeStatus, Error

具体是Error属性,因为并不总是出现错误状态。简而言之,您需要解决变量$errmsg的内容,这是填充上述属性的内容。在循环的每次传递中,您应该重置或清除变量。有这种思路的几种选择。

  1. 在每个循环传递开始时将变量设置为空字符串

    foreach ($Computer in $Computers) {
        $Computer   =   $Computer.toupper()
        $Isonline   =   "OFFLINE"
        $Status     =   "SUCCESS"
        $errmsg     =   ""
    .... more code stuff...
    
  2. 您还可以在try块之前或输出行之后的类似位置使用cmdlet Clear-Variable注意有意省略$

    Clear-Variable errmsg
    

答案 1 :(得分:1)

我完全支持马特的回答!这两个选项中的任何一个都是解决问题的简单方法。我提供的更多是重写,对某些事情的完成有一些明确的改变。我放弃了流编写器或者收集结果,然后使用Set-Content将它们转储到文件的末尾(为什么要锁定整个进程的文件,并在你可以立即执行所有操作时写入一些文件。结束?)。此外,我在每个循环的开头创建您的计算机对象,并同时跳过$IsOnline$Status$errmsg变量。然后对于每个循环,我将该对象添加到一个数组(在循环之前创建),最后我输出它。

[cmdletbinding()]
param (
[parameter(mandatory = $true)]
$InputFile,
$OutputDirectory
)

#Log failures in an output file called "failed-computers.txt" stored in the directory of input file
if(!$outputdirectory) {
    $outputdirectory = (Get-Item $InputFile).directoryname
}   
$failedcomputers    =   Join-Path $outputdirectory "failed-computers.txt"

#Prompt to confirm password twice and read password in a secure manner
$password = Read-Host "Enter the password" -AsSecureString
$confirmpassword = Read-Host "Confirm the password" -AsSecureString

#Convert password into plain text for comparison
$pwd1_text = [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($password))
$pwd2_text = [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($confirmpassword))

#If the passwords don't match, script exits; otherwise, it continues.
if($pwd1_text -ne $pwd2_text) {
    Write-Error "Entered passwords are not same. Script is exiting."
    exit
}

#Check if input file exists. If file not found, script exits.
if(!(Test-Path $InputFile)) {
    Write-Error "File ($InputFile) not found. Script is exiting."
    exit
}

#Read contents of input file using Get-Content cmdlet and store list in an array called $computers
$Computers = Get-Content -Path $InputFile

#Create an empty array to populate with the results of the password updates
$Results = @()

#Loop through each computer account in the array and check if online or not using Test-Connection cmdlet
foreach ($ComputerName in $Computers) {
    #Create Computer object
    $Computer   =   New-Object PSObject -Prop @{
        'Name'     =   $ComputerName.toupper()
        'Isonline' =   "OFFLINE"
        'Status'   =   "SUCCESS"
        'Error'    =   ""
    }
    Write-Verbose "Working on $ComputerName"
    if((Test-Connection -ComputerName $Computer.Name -count 1 -ErrorAction 0)) {
        $Computer.Isonline = "ONLINE"
        Write-Verbose "`t$ComputerName is Online"
    } else { Write-Verbose "`t$ComputerName is OFFLINE" }

    #If ping is successful, password is changed
    try {
        $account = [ADSI]("WinNT://$ComputerName/Administrator,user")
        $account.psbase.invoke("setpassword",$pwd1_text)
        Write-Verbose "`tPassword Change completed successfully"
    }

    #If password change fails, respective error is recorded
    catch {
        $Computer.status = "FAILED"
        Write-Verbose "`tFailed to Change the administrator password. Error: $_"
        $e = $_.Exception
        $Computer.Error = $e.Message
        while ($e.InnerException) {
            $e = $e.InnerException
            $Computer.Error = $e.Message
        }
    }
    #Display resutls to screen
    $Computer

    #Add computer's results/errors to results array
    $Results += $Computer

}

#Write results to file
$Results|Format-Table -AutoSize|Out-String|Set-Content $failedcomputers
$Results|Export-Csv ($failedcomputers -replace "txt$","csv") -NoTypeInformation
Write-Host "`n`nFailed computers list is saved to $failedcomputers, with CSV export of the same name and location"

哦,是的,我还将整个内容输出为CSV,以便在以后需要使用它时可以在Excel中轻松打开(过滤无法更新,脱机或其他任何内容的系统)。 / p>