【问题标题】:How can I output all the output of a called command in Powershell script如何在 Powershell 脚本中输出调用命令的所有输出
【发布时间】:2015-06-15 12:24:20
【问题描述】:

我已经修改了现有的 Powershell 脚本,以使用 quser /server:{servername} 查询已登录(或断开连接)用户会话的服务器列表,并将结果输出到 CSV 文件中。它确实输出任何登录的用户,但它没有捕获的是有 0 个用户或无法访问的服务器(服务器脱机、rpc 不可用等)。我假设这是因为这些其他条件是“错误”而不是命令输出。

因此,如果它访问没有用户的服务器,它会在运行脚本的控制台中输出“No User exists for *”。如果它遇到无法访问的服务器,则会输出“错误 0x000006BA 枚举会话名称”和第二行“错误 [1722]:RPC 服务器不可用”。在运行脚本的控制台中。所以这些条件都没有显示在 output.csv 文件中。

我想知道是否有人可以建议我如何在 CSV 中将这些条件捕获为“$Computer 没有用户”和“$Computer 无法访问”

这是脚本

$ServerList = Read-Host 'Path to Server List?'
$ComputerName = Get-Content -Path $ServerList
    foreach ($Computer in $ComputerName) {
        quser /server:$Computer | Select-Object -Skip 1 | ForEach-Object {
            $CurrentLine = $_.Trim() -Replace '\s+',' ' -Split '\s'
            $HashProps = @{
                UserName = $CurrentLine[0]
                ServerName = $Computer
            }

            # If session is disconnected different fields will be selected
            if ($CurrentLine[2] -eq 'Disc') {
                    $HashProps.SessionName = $null
                    $HashProps.Id = $CurrentLine[1]
                    $HashProps.State = $CurrentLine[2]
                    $HashProps.IdleTime = $CurrentLine[3]
                    $HashProps.LogonTime = $CurrentLine[4..6] -join ' '
            } else {
                    $HashProps.SessionName = $CurrentLine[1]
                    $HashProps.Id = $CurrentLine[2]
                    $HashProps.State = $CurrentLine[3]
                    $HashProps.IdleTime = $CurrentLine[4]
                    $HashProps.LogonTime = $CurrentLine[5..7] -join ' '
            }

            New-Object -TypeName PSCustomObject -Property $HashProps |
            Select-Object -Property ServerName,UserName,State,LogonTime |
            ConvertTo-Csv -NoTypeInformation | select -Skip 1 | Out-File -Append .\output.csv
        }
    } 

任何人好奇我为什么使用ConvertTo-CSV 而不是Export-CSV 会更干净,因为我运行它的服务器正在运行Powershell 2.0,其中Export-CSV 不支持-Append。我不担心输出可以满足我的需要,但是如果有人对此有更好的建议,请随时发表评论。

【问题讨论】:

    标签: powershell csv output


    【解决方案1】:

    所以我们对脚本进行了一些更新。如果使用 quser 出现任何错误,我们会将其捕获为一个特殊条目,其中服务器名称将读取 "Error contacting $computer" 和其他将为错误提供上下文的文本..

    $ServerList = Read-Host 'Path to Server List?'
    $ComputerNames = Get-Content -Path $ServerList
    $ComputerNames | ForEach-Object{
        $computer = $_
        $results = quser /server:$Computer 2>&1 | Write-Output
        If($LASTEXITCODE -ne 0){
            $HashProps = @{
                UserName = ""
                ServerName = "Error contacting $computer"
                SessionName = ""
                Id = ""
                State = $results | Select-String -Pattern '\[.+?\]' | Select -ExpandProperty Matches | Select -ExpandProperty Value
                IdleTime = ""
                LogonTime = ""
            }
    
            switch -Wildcard ($results){
                '*[1722]*'{$HashProps.UserName = "RPC server is unavailable"}
                '*[5]*'{$HashProps.UserName = "Access is denied"}
                default{$HashProps.UserName = "Something else"}
            }
        } Else {
            $results | Select-Object -Skip 1 | ForEach-Object {
                $CurrentLine = $_.Trim() -Replace '\s+',' ' -Split '\s'
                $HashProps = @{
                    UserName = $CurrentLine[0]
                    ServerName = $Computer
                }
    
                # If session is disconnected different fields will be selected
                if ($CurrentLine[2] -eq 'Disc') {
                        $HashProps.SessionName = $null
                        $HashProps.Id = $CurrentLine[1]
                        $HashProps.State = $CurrentLine[2]
                        $HashProps.IdleTime = $CurrentLine[3]
                        $HashProps.LogonTime = $CurrentLine[4..6] -join ' '
                } else {
                        $HashProps.SessionName = $CurrentLine[1]
                        $HashProps.Id = $CurrentLine[2]
                        $HashProps.State = $CurrentLine[3]
                        $HashProps.IdleTime = $CurrentLine[4]
                        $HashProps.LogonTime = $CurrentLine[5..7] -join ' '
                }
            }
    
        }
        New-Object -TypeName PSCustomObject -Property $HashProps 
    } | Select-Object -Property ServerName,UserName,State,LogonTime |
        Export-Csv -NoTypeInformation .\output.csv 
    

    部分问题在于,由于这不是 PowerShell cmdlet 捕获 stderr 以便解析它需要以不同的方式工作。使用$erroractionpreference 也是一种选择,但这是初稿。我们使用2>&1 将错误捕获到$results 以隐藏屏幕上的消息。然后我们使用If 来查看最后一个命令是否成功。

    发生错误时

    我使用了带有错误文本的 switch 语句。所以你可以根据$results中返回的文本来定制输出

    一些小改动

    您的其余代码大部分是相同的。我将对象创建移到 If 语句之外,以便可以记录错误并将其更改为 Export-CSV,因为 PowerShell 将为您计算出详细信息。

    除非您打算随着时间的推移多次传递此函数并将其捕获到同一个文件中。

    导出前的控制台输出

    ServerName             UserName                  State  LogonTime       
    ----------             --------                  -----  ---------       
    serverthing01          bjoe                      Active 4/9/2015 5:42 PM
    Error contacting c4093 RPC server is unavailable [1722]                 
    Error contacting c4094 Access is denied          [5]    
    

    您可以看到每个服务器都有输出,即使最后两个服务器有不同的原因导致没有正确的输出。如果您曾看到 "Something else",则表示存在一个错误,该错误没有附加特定消息。

    如果发生这种情况,请查看State 并显示错误编号。然后你只需要相应地更新Switch

    重大更新

    我不确定为多个用户删除额外行的问题是什么,但我已经有一个dynamic parsing code for positionally delimited text,所以我把它带到这里。

    Function ConvertFrom-PositionalText{
        param(
            [Parameter(Mandatory=$true)]
            [string[]]$data
        )
        $headerString = $data[0]
        $headerElements = $headerString -split "\s+" | Where-Object{$_}
        $headerIndexes = $headerElements | ForEach-Object{$headerString.IndexOf($_)}
    
        $data | Select-Object -Skip 1 | ForEach-Object{
            $props = @{}
            $line = $_
            For($indexStep = 0; $indexStep -le $headerIndexes.Count - 1; $indexStep++){
                $value = $null            # Assume a null value 
                $valueLength = $headerIndexes[$indexStep + 1] - $headerIndexes[$indexStep]
                $valueStart = $headerIndexes[$indexStep]
                If(($valueLength -gt 0) -and (($valueStart + $valueLength) -lt $line.Length)){
                    $value = ($line.Substring($valueStart,$valueLength)).Trim()
                } ElseIf ($valueStart -lt $line.Length){
                    $value = ($line.Substring($valueStart)).Trim()
                }
                $props.($headerElements[$indexStep]) = $value    
            }
        New-Object -TypeName PSCustomObject -Property $props
        } 
    }
    
    $ServerList = Read-Host 'Path to Server List?'
    $ComputerNames = Get-Content -Path $ServerList
    $HashProps = @{}
    $exportedprops = "ServerName","UserName","State",@{Label="LogonTime";Expression={$_.Logon}}
    $ComputerNames | ForEach-Object{
        $computer = $_
        $results = quser /server:$Computer 2>&1 | Write-Output
        If($LASTEXITCODE -ne 0){
            $HashProps = @{
                UserName = ""
                ServerName = "Error contacting $computer"
                SessionName = ""
                Id = ""
                State = $results | Select-String -Pattern '\[.+?\]' | Select -ExpandProperty Matches | Select -ExpandProperty Value
                Idle = ""
                Time = ""
                Logon = ""
            }
    
            switch -Wildcard ($results){
                '*[1722]*'{$HashProps.UserName = "RPC server is unavailable"}
                '*[5]*'{$HashProps.UserName = "Access is denied"}
                default{$HashProps.UserName = "Something else"}
            }
    
            New-Object -TypeName PSCustomObject -Property $HashProps
        } Else {
    
            ConvertFrom-PositionalText -data $results  | Add-Member -MemberType NoteProperty -Name "ServerName" -Value $computer -PassThru 
        }
    
    } | Select-Object -Property $exportedprops |
        Export-Csv -NoTypeInformation .\output.csv 
    

    这里最大的不同是我们使用ConvertFrom-PositionalText 来解析来自quser 的细节。需要将 $HashProps = @{} 清零,这会导致多次运行的结果相互冲突。为了获得良好的测量结果,函数的输出和虚拟误差数据具有相同的参数集。使用了$exportedprops,它有一个计算表达式,这样你就可以获得你想要的标题。

    新输出

    ServerName             USERNAME                  STATE  LogonTime        
    ----------             --------                  -----  ---------        
    server01               user1                     Disc   3/12/2015 9:38 AM
    server01               user2                     Active 4/9/2015 5:42 PM 
    Error contacting 12345 Access is denied          [5]                     
    Error contacting 12345 RPC server is unavailable [1722]                  
    svrThg1                user3                     Active 4/9/2015 5:28 PM 
    svrThg1                user4                     Active 4/9/2015 5:58 PM 
    svrThg1                user-1                    Active 4/9/2015 9:50 PM 
    svrThg1                bjoe                      Active 4/9/2015 10:01 PM
    

    【讨论】:

    • 您为我提供了我所需要的一个小问题,我相信我可以轻松解决这个问题。如果服务器有多人登录(这些是 Citrix 服务器),它只会在 CSV 中记录其中一个用户(它枚举的最后一个用户)。这正是我遇到的问题,需要通过 Powershell 2.0 中的 -Append 来解决。
    • 嗯...我注意到我之前得到了多个结果。我会回顾一下,看看可能丢失了什么。
    • 这是我对一台我知道有两个用户登录的服务器运行命令时得到的结果。 PS C:\temp> quser /server:myserversname USERNAME SESSIONNAME ID STATE IDLE TIME LOGON TIME管理员 168 光盘无 2015 年 2 月 12 日下午 6:10 用户名 170 光盘无 2015 年 2 月 17 日下午 2:37 但它在 CSV 中唯一记录“用户名”。
    • 如果 Powershell 2.0 是根本原因,我可能只需将正在运行它的一台机器升级到至少 3.0。也许这会解决我的问题。由于其他组织问题,我无法将所有机器升级到 Powershell 3.0。
    • @WilliamJenkins 检查我的更新。很确定它适用于 2.0。如果不让我知道,我可以安装一个 2.0 系统进行检查。经过测试,它可以与多个用户一起使用,并且仍然可以纠正错误。
    猜你喜欢
    • 2019-10-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多