powershell - 如何在Powershell脚本中输出被调用命令的所有输出

标签 powershell csv output

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

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

我想知道是否有人可以建议我如何在 CSV 中捕获这些条件,如“$Computer 没有用户”和“$Computer Unreachable”

这是脚本

$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。我并不担心输出是否能满足我的需要,但如果有人对此有更好的建议,请随时发表评论。

最佳答案

我们对脚本进行了一些更新。如果使用 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 

问题的一部分是,由于这不是一个捕获 stderr 以便解析的 PowerShell cmdlet,因此它需要以不同的方式工作。使用 $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]    

您可以看到每个服务器都有输出,即使最后两个服务器有不同的原因导致没有正确的输出。如果您看到“其他内容”,则表明存在错误,且错误中没有附加特定消息。

发生这种情况时,请查看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 ,它具有计算表达式,以便您可以获得所需的 header 。

新输出

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

关于powershell - 如何在Powershell脚本中输出被调用命令的所有输出,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29550324/

相关文章:

python - 从 python 中的其他 csv 列追加到新的 csv

c - 将 CSV 数据解析为结构

javascript - 使用 javascript 取消表单提交不起作用

pipe - 如何将 Gnuplot 输出直接定向到文件描述符(例如管道)?

arrays - Powershell 阵列范围;为什么我的数组是空的?

powershell - 过滤 cmd 命令的输出,而无需使用中间文件

python - 使用 pandas 或其他 python 模块读取特定列

java - 从文件读取时获取与写入文件不同的字节数组

powershell - Powershell:在AD中检查重复的电子邮件

powershell - 从 2 个哈希表中选择重复的键值